basquetWi + New ticket
terra TERRA-111

Non-Google-address onboarding requires actual Google sign-in verification and a unified replacement flow

Blocked normal ctcoder-terra-cc ⛔ Esquema previousEmail y transporte/outbox estan disponibles, pero falta definir y construir el flujo unico en onboarding, administracion y /sin-acceso sin asumir que un dominio no-Google no puede autenticarse. La identidad Google real solo se confirma mediante intento de ingreso; tambien queda sin ejercer la entrega real de correo de TERRA-59.

Elazar, 2026-08-23 CLI, verbatim intent: users whose address is not a Google-auth address get an email asking them to supply a UBA or Gmail address; this belongs to onboarding, user management AND the access-request path. His explicit constraint, and it is the part most likely to be got wrong: "dont make the mistake of keeping old non-google auth gmail as main or display or whatever and adding gmail on top of it, gmail or .edu.ar REPLACES old email and old email moves to secondary email field." MEASURED LIVE 2026-08-23 (read-only), because the size of this decides its shape: 159 terra users, and only 4 are linked to GoTrue at all (supabaseAuthId NOT NULL). gmail.com 113 (1 linked) · odontologia.uba.ar 7 (2 linked) · cbc.uba.ar 1 hotmail.com 28 · yahoo.com.ar 3 · outlook.com 2 · hotmail.com.ar 1 institutoferrari.com 1 · um.edu.ar 1 · clinicabraverman.com 1 · pensanta.com 1 (linked) So 34 users sit on consumer domains that are NOT Google-native, plus 3 on custom domains of unknown provider. That is the population this WI exists for, and it is ~23% of the roster - not an edge case. DO NOT ENCODE "NOT GMAIL = CANNOT SIGN IN". IT IS FALSE. A Google Account can be created with ANY address as its username, hotmail included, so some of those 34 may already sign in perfectly. Terra cannot tell which from its own data - all 34 have supabaseAuthId NULL and lastSignInAt NULL, and lastSignInAt is never written anyway (TERRA-80), so BOTH columns are silent for the same person whether they are able to sign in or not. There is no Google API that answers "is this address a Google Account" for a third party. THE ONLY TEST IS AN ACTUAL SIGN-IN ATTEMPT. Design the flow so the address is a HINT that triggers an offer, never a verdict that blocks a login - a user wrongly classified as unable to sign in, and then blocked, is a worse outcome than the one being fixed. SCHEMA: THE COLUMN DOES NOT EXIST AND THE TABLE IS NOT OURS. public.users today holds id, supabaseAuthId, email, emailCanonical, legajo, documentoType, documentoNumber, nombre, apellido, telefono, isSynthetic, syntheticSessionId, createdAt, updatedAt, deletedAt, lastSignInAt, profilePictureRef. There is NO secondary/previous email column. users is SHARED WITH ENAMEL, so adding one is a change to every writer of that table and needs db-enamel-cc GO before any DDL - it is not a terra-local migration. Also note emailCanonical exists and the swap must keep it consistent with email, or the canonical index starts disagreeing with the address people actually use. THE SWAP IS THE RISKY OPERATION, not the email. Replacing the primary address on a row that other things key on is exactly where a half-applied change hides: emailCanonical, the enamel co-tenant's own reads, any access-request ticket already filed under the old address, and the GoTrue identity if one is ever linked. The old address must land in the secondary field in the SAME transaction that rewrites the primary, never as a second step - and the archive/GUC contracts apply because this is an UPDATE on a shared table that erases the previous value (TERRA-28: an UPDATE overwrites with zero trace unless the archive path is armed). COPY, to be reviewed rather than shipped as drafted here - Elazar's own sentence was "Por favor enviar una direcion de uba.edu o gmail para poder usar la app" and he asked for it properly worded. Note "uba.edu" is not the real domain; the live ones are odontologia.uba.ar and cbc.uba.ar. Draft: Asunto: Necesitamos otra dirección de correo para tu acceso a Terra Terra usa el inicio de sesión de Google. Para poder ingresar necesitamos una dirección de Google: tu cuenta institucional de la UBA (@odontologia.uba.ar) o una cuenta de Gmail. Respondé este correo indicando la dirección que vas a usar. Esa pasa a ser tu dirección principal en Terra y conservamos la actual como alternativa. Si ya podés ingresar con la dirección que tenés, avisanos y no cambiamos nada. That last line exists BECAUSE of the false-negative risk above. THREE SURFACES, one rule, and they are named separately because they are three different code paths: onboarding (a user created by the cátedra who has never signed in), user management (administrar/usuarios - an admin looking at a row should SEE that the address is not Google-native and be able to trigger the ask), and the access-request path (/sin-acceso - someone asking for access from an address that will not work should be told so at that moment, not later by email). PARRAFO CORREGIDO 2026-09-01. Lo que decia aca, y que quedo tres dias contradiciendo la propia razon de bloqueo de esta misma fila: "BLOCKED ON TERRA-59. This is a mail-sending feature and terra cannot send mail: no SMTP_USER/SMTP_PASS in the production environment, measured 2026-08-23. Whatever is built here is inert until those two variables land." No lo borro: esa medicion existio, fue del 2026-08-22 contra vercel env ls production, y dio cinco nombres ninguno de correo. Borrarla destruiria lo unico que la hacia auditable, que es que alguien midio y anoto cuando. QUE LA SUPERA: el registro de fuente del 2026-08-23 en src/lib/access-request-actions.ts, donde las dos variables figuran instaladas. La razon de bloqueo de esta fila ya lo decia desde el 2026-08-29 y la descripcion siguio diciendo lo contrario debajo. Una fila que se contradice a si misma se resuelve por el lado que tiene mas texto, y ese era el parrafo viejo. CORRIJO ESTE PARRAFO EL MISMO DIA QUE LO ESCRIBI. Decia: "LO QUE NO AFIRMO, y es la unica forma honesta de escribirlo: que el transporte este configurado HOY. Un comentario de codigo es el REGISTRO de una medicion, no la medicion. Nadie volvio a mirar esas variables desde el 2026-08-23 y en este host no hay CLI de vercel para mirarlas." La primera mitad sigue siendo cierta y vale para cualquier fila: un comentario de codigo es el registro de una medicion y un registro fechado superado por otro registro fechado no establece nada sobre el presente. LA SEGUNDA MITAD ERA FALSA: si habia con que medirlo. Mire el instrumento que se me ocurrio primero, el CLI de vercel, en vez de preguntarme que cosa observa ese sistema. La app publica /api/health y ese endpoint lee la configuracion de correo del entorno vivo. Medido por mi el 2026-09-01T07:24:41Z sobre el deploy corriendo: version 0.31.28, mailOk true, mailError null, mailFromName true. ALCANCE DEL INSTRUMENTO, dicho para no repetir el error en la otra direccion: mailOk sale de readMailConfig, que lee las variables de entorno y valida su forma, y NO abre conexion SMTP ni manda nada. Queda establecido que produccion tiene hoy SMTP_USER y SMTP_PASS con forma valida; NO queda establecido que un mail salga. POR QUE SIGUE BLOQUEADA IGUAL, con la razon corregida: no por falta de credencial sino porque el camino de correo NUNCA se ejercito end to end - eso es TERRA-59, y ahi se ve que la cola de pedidos si tiene una direccion viva desde el 2026-08-22. Mas la mitad de diseño del flujo de onboarding, que no depende de correo en absoluto.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • wi-cli-venus created · 7w ago
  • wi-cli-venus blocked · 7w ago
    Blocked on TERRA-59 (no SMTP transport in production) for the mail leg, and on a db-enamel-cc GO for the schema leg - users is a SHARED table and there is no secondary-email column today. The three UI surfaces (onboarding, administrar/usuarios, /sin-acceso) are buildable ahead of both, but shipping the ask without a transport reproduces the exact defect TERRA-59 exists for: a promise with nothing behind it.
  • wi-cli-venus commented · 7w ago
    PASART, settled 2026-08-23 on Elazar's instruction: jordipasart@hotmail.com is his address AS DOCENTE. The terra row already exists (fe171c44-d89f-49a7-b8cb-39bea7f238f5, Jorge Pasart) and already carries BOTH alumno and docente in terra. Elazar named the docente use; he did NOT ask for alumno to be removed, so nothing is dropped on inference - the enrolment register does list him as a 2023 student with an inscription fee and a beca, so both memberships may be correct. HE IS ALSO THE HEADLINE CASE FOR THIS WI: hotmail.com, supabaseAuthId NULL, never signed in - one of the 28 hotmail addresses. If the cátedra's own Modalidad director cannot sign in, that is the first person the flow in this WI has to serve.
  • wi-cli-venus commented · 7w ago
    SCHEMA ASK ROUTED TO THE ENAMEL SIDE, and the DM FAILED - recording the route so it is not assumed delivered. mcp send to db-enamel-cc returned recipient_not_registered: the agent has never registered, and the hub does NOT queue mail for an unregistered name. That is different from the buffering that happens for a registered-but-idle agent, and it means 'send it now so it picks it up on spawn' does not work. Durable drop left instead at /resource/terra/handoff/2026-08-23-for-db-enamel-cc-terra111-users-secondary-email.md (venus local disk, mounted on all three hosts), which states the ask, the live measurements, the current column list and the four rulings enamel owns: (1) does enamel want the column at all, given a shared-table column changes every writer of users including theirs, versus terra keeping an app-local mapping; (2) nullable text - any NOT NULL or default is a change to every existing writer; (3) emailCanonical must stay consistent with email through the swap or the canonical index disagrees with the address people log in with, and terra must be told if enamel derives it by trigger rather than guessing; (4) the swap is an UPDATE that ERASES the previous primary, terra's archive/GUC contract does not cover a table terra does not own, so which archive path applies - or whether enamel considers an email change something that need not leave a trace. RE-SEND THE DM ONCE db-enamel-cc APPEARS IN roster. Until an enamel ruling exists this WI has no implementable schema, independent of the TERRA-59 transport block.
  • wi-cli-venus commented · 7w ago
    ENAMEL RULINGS RECEIVED AND SETTLED 2026-08-23 (db-enamel-cc, tags mt5ocrqzpgyr + mt5odu8fxfdf). users is enamel's table; these are theirs to make and terra treats them as binding. 1. COLUMN APPROVED: `previousEmail text NULL` on public.users. An app-local mapping in terra was rejected on their reasoning, which I record because it is the better argument: it would duplicate identity data terra does not own. 2. Nullable, NO default, NO NOT NULL. A NOT NULL here would be a change to every existing writer of a shared table. 3. REVISED AFTER I MEASURED IT, and this one would have failed at runtime every time rather than drifting quietly. Their first ruling was that emailCanonical is caller-set and terra's swap must write it, on the evidence that pg_trigger shows no trigger on users touching it. TRUE, AND THE WRONG INSTRUMENT: emailCanonical is a STORED GENERATED column, `attgenerated='s'`, `pg_get_expr` = "canonicalizeEmail"(email) - a generated-column expression is not a trigger and pg_trigger cannot see one. An UPDATE naming emailCanonical raises 428C9 "cannot insert into column ... it is a generated column". db-enamel-cc re-checked pg_attribute independently and revised. FINAL: the swap sets email AND previousEmail ONLY, never emailCanonical. It re-derives, and divergence is not merely unlikely but INEXPRESSIBLE - which satisfies the intent of the original ruling by the opposite instruction. 4. NO SEPARATE ARCHIVE PATH. previousEmail on the row IS the trace; that is what retaining it is for. No appEvents row - an email swap carries no patient identifier and does not need audit-log treatment. (Consistent with the standing never-write-personal-data-to-appEvents rule: the address is exactly the kind of value that must not land there.) ACCEPTED BOUND, ruled explicitly rather than discovered later: previousEmail is a SINGLE SLOT. A second address change overwrites the first and that history is gone with no trace anywhere - and ruling 4 makes this column the only trace, so the two decisions interact. db-enamel-cc accepted it: not worth a history table for an edge case this rare. Recorded here so the next person meets it as a decision rather than as a surprise. METHOD NOTE WORTH MORE THAN THE RULING: the wrong answer came from an instrument that could not observe the mechanism it was asked about, and it answered honestly. "No trigger touches it" reads exactly like a measurement. Terra's own source already said GENERATED ALWAYS - I checked pg_attribute rather than quoting that, because the source could equally have been stale, and the live catalog is the only thing that settles it. REMAINING BLOCKS ON THIS WI ARE UNCHANGED: TERRA-59 (no SMTP transport in production). The schema question is now answered and is no longer a blocker; the DDL goes to db-enamel-cc, who applies it as an enamel-side migration.
  • wi-cli-venus blocked · 7w ago
    Blocked on TERRA-59 only (no SMTP transport in production) for the mail leg. The schema leg (secondary-email column) is SETTLED 2026-08-23 by db-enamel-cc rulings (tags mt5ocrqzpgyr + mt5odu8fxfdf): previousEmail text NULL approved on public.users, no default/NOT NULL, swap sets email+previousEmail only (emailCanonical is a generated column, not writable), no separate archive path. This blockedReason previously still listed the schema leg as open; the WI's own event log already recorded it resolved on 2026-08-23. Correcting the reason to match, not reopening or closing anything.
  • wi-cli-venus assigned · 7w ago
    coder-terra-ca
  • wi-cli-venus blocked · 6w ago
    Reason corrected 2026-08-29 from an audit-terra-ca source reading. The old reason said the mail leg was blocked on TERRA-59 because there is no SMTP transport in production. That premise is stale: src/lib/access-request-actions.ts:180-190 shows production SMTP_USER/SMTP_PASS are set, and src/lib/email/recipients.ts:76-104 returns a nonempty COORDINATION_EMAILS literal before it ever reaches the null-group branch. Transport is CONFIGURED. What is actually missing is that nothing has ever exercised it end to end - see TERRA-59, retitled the same day for exactly that. Schema leg stays settled (previousEmail approved 2026-08-23 by db-enamel-cc; emailCanonical is generated and not writable). So this WI is blocked on an UNEXERCISED path plus the onboarding-flow design, not on a missing credential. Note the stale premise ALSO survives in source comments at recipients.ts:14-19 and app/administrar/page.tsx:209-217 - those still tell a reader the SMTP is missing, and they are now wrong.
  • wi-cli-venus note · 6w ago
    SUBTAREA DE FUENTE ASIGNADA a coder-terra-ca: corregir los dos comentarios que este blocker ya nombra. Salio del barrido codigo-contra-tablero de coder-terra-ca y lo verifique leyendo los tres archivos antes de aceptarlo. LO QUE HAY, y la parte que importa es que NO ES UNA CONTRADICCION. recipients.ts:14-19 y administrar/page.tsx dicen que produccion no tiene SMTP_USER/SMTP_PASS, medido contra vercel env ls production el 2026-08-22. access-request-actions.ts:180-190 dice que se pusieron el 2026-08-23. Las dos primeras no son falsas: son VIEJAS. La distincion decide como se arregla - una afirmacion fechada solo puede envejecer, nunca volverse mentira, y por eso la correccion no es borrarla. CONDICION QUE LE PUSE: que no se borre la medicion del 22. Reescribir el parrafo como si el estado siempre hubiera sido este destruye lo unico que hace auditable a un comentario, que es que alguien midio y dejo constancia de cuando. Cada afirmacion sigue fechada y la nueva dice que reemplaza a la vieja. LIMITE QUE NO PUEDO CERRAR Y QUE QUEDA ESCRITO EN VEZ DE REDONDEADO: no pude re-medir. El CLI de vercel no esta instalado en este host, asi que todo lo que tengo son tres comentarios de codigo, y un comentario no es una medicion del entorno de produccion sino la constancia de una. NADIE VERIFICO ESAS VARIABLES DESDE EL 2026-08-23. Le pedi que el comentario nuevo diga eso en lugar de afirmar planamente que el transporte esta configurado: si resulta que no lo esta, la diferencia entre las dos redacciones es quien queda enganado. No se abre fila nueva - este blocker ya nombra los dos comentarios, asi que es subtarea de aca.
  • wi-cli-venus verified · 6w ago
    SUBTAREA DE FUENTE CERRADA. coder-terra-ca pusheo d988f9a, auditado por mi contra la fuente y no aceptado por reporte: lista de archivos traida por gh api, comentarios leidos en el checkout. CUATRO ARCHIVOS, no los dos que yo habia nombrado - recipients.ts, access-notify.ts, access-request-actions.ts y administrar/page.tsx. La ampliacion es correcta: la fila describe una CLASE de comentario vencido, no una lista de dos, y los otros dos son el mismo defecto en archivos que yo no habia mirado. LA FORMA ES LA QUE PEDI Y ES LA QUE IMPORTA. recipients.ts encabeza con que es evidencia historica y no una afirmacion de produccion actual, conserva la medicion del 2026-08-22 CON SU METODO - vercel env ls production, cinco nombres, ninguno de correo - dice que la supera el registro del 2026-08-23, y cierra con que ninguna de las dos se re-midio hoy. access-notify.ts agrega algo que no pedi y esta bien: que esto no afirma que un envio pueda o no ocurrir ahora. CHEQUEE QUE NO SOBREVIVIERA NINGUNA AFIRMACION PLANA, porque corregir cuatro y dejar una quinta es peor que no corregir - el lector encuentra la contradiccion y no tiene como saber cual gana. Grep sobre src/ por las formas de 'no hay SMTP' devuelve un unico hit y esta ADENTRO del parrafo fechado que describe la medicion del 22. No queda ninguna suelta. LO QUE SIGUE SIN ESTABLECERSE Y AHORA ESTA ESCRITO EN LOS CUATRO ARCHIVOS: nadie sabe hoy si produccion tiene esas variables. No se re-midio, el CLI de vercel no esta instalado en este host, y un comentario de codigo es la constancia de una medicion, no la medicion. El bloqueo de esta fila no se mueve por esto - sigue en el camino no ejercitado punta a punta de TERRA-59 mas el diseno del flujo de onboarding.
  • wi-cli-venus descriptionChanged · 6w ago
    was: Elazar, 2026-08-23 CLI, verbatim intent: users whose address is not a Google-auth address get an email asking them to supply a UBA or Gmail address; this belongs to onboarding, user management AND the access-request path. His explicit constraint, and it is the part most likely to be got wrong: "dont make the mistake of keeping old non-google auth gmail as main or display or whatever and adding gmail on top of it, gmail or .edu.ar REPLACES old email and old email moves to secondary email field." MEASURED LIVE 2026-08-23 (read-only), because the size of this decides its shape: 159 terra users, and only 4 are linked to GoTrue at all (supabaseAuthId NOT NULL). gmail.com 113 (1 linked) · odontologia.uba.ar 7 (2 linked) · cbc.uba.ar 1 hotmail.com 28 · yahoo.com.ar 3 · outlook.com 2 · hotmail.com.ar 1 institutoferrari.com 1 · um.edu.ar 1 · clinicabraverman.com 1 · pensanta.com 1 (linked) So 34 users sit on consumer domains that are NOT Google-native, plus 3 on custom domains of unknown provider. That is the population this WI exists for, and it is ~23% of the roster - not an edge case. DO NOT ENCODE "NOT GMAIL = CANNOT SIGN IN". IT IS FALSE. A Google Account can be created with ANY address as its username, hotmail included, so some of those 34 may already sign in perfectly. Terra cannot tell which from its own data - all 34 have supabaseAuthId NULL and lastSignInAt NULL, and lastSignInAt is never written anyway (TERRA-80), so BOTH columns are silent for the same person whether they are able to sign in or not. There is no Google API that answers "is this address a Google Account" for a third party. THE ONLY TEST IS AN ACTUAL SIGN-IN ATTEMPT. Design the flow so the address is a HINT that triggers an offer, never a verdict that blocks a login - a user wrongly classified as unable to sign in, and then blocked, is a worse outcome than the one being fixed. SCHEMA: THE COLUMN DOES NOT EXIST AND THE TABLE IS NOT OURS. public.users today holds id, supabaseAuthId, email, emailCanonical, legajo, documentoType, documentoNumber, nombre, apellido, telefono, isSynthetic, syntheticSessionId, createdAt, updatedAt, deletedAt, lastSignInAt, profilePictureRef. There is NO secondary/previous email column. users is SHARED WITH ENAMEL, so adding one is a change to every writer of that table and needs db-enamel-cc GO before any DDL - it is not a terra-local migration. Also note emailCanonical exists and the swap must keep it consistent with email, or the canonical index starts disagreeing with the address people actually use. THE SWAP IS THE RISKY OPERATION, not the email. Replacing the primary address on a row that other things key on is exactly where a half-applied change hides: emailCanonical, the enamel co-tenant's own reads, any access-request ticket already filed under the old address, and the GoTrue identity if one is ever linked. The old address must land in the secondary field in the SAME transaction that rewrites the primary, never as a second step - and the archive/GUC contracts apply because this is an UPDATE on a shared table that erases the previous value (TERRA-28: an UPDATE overwrites with zero trace unless the archive path is armed). COPY, to be reviewed rather than shipped as drafted here - Elazar's own sentence was "Por favor enviar una direcion de uba.edu o gmail para poder usar la app" and he asked for it properly worded. Note "uba.edu" is not the real domain; the live ones are odontologia.uba.ar and cbc.uba.ar. Draft: Asunto: Necesitamos otra dirección de correo para tu acceso a Terra Terra usa el inicio de sesión de Google. Para poder ingresar necesitamos una dirección de Google: tu cuenta institucional de la UBA (@odontologia.uba.ar) o una cuenta de Gmail. Respondé este correo indicando la dirección que vas a usar. Esa pasa a ser tu dirección principal en Terra y conservamos la actual como alternativa. Si ya podés ingresar con la dirección que tenés, avisanos y no cambiamos nada. That last line exists BECAUSE of the false-negative risk above. THREE SURFACES, one rule, and they are named separately because they are three different code paths: onboarding (a user created by the cátedra who has never signed in), user management (administrar/usuarios - an admin looking at a row should SEE that the address is not Google-native and be able to trigger the ask), and the access-request path (/sin-acceso - someone asking for access from an address that will not work should be told so at that moment, not later by email). BLOCKED ON TERRA-59. This is a mail-sending feature and terra cannot send mail: no SMTP_USER/SMTP_PASS in the production environment, measured 2026-08-23. Whatever is built here is inert until those two variables land.
  • wi-cli-venus blocked · 6w ago
    Corregida por segunda vez 2026-09-01. La razon del 2026-08-29 decia textual "Transport is CONFIGURED", y esa afirmacion es mas fuerte que la evidencia que la sostiene. Lo que existe es un registro de fuente del 2026-08-23 en src/lib/access-request-actions.ts:180-190 donde las dos variables figuran instaladas. Un comentario de codigo es el REGISTRO de una medicion, no la medicion: nadie volvio a mirar el entorno de produccion desde entonces y en este host no hay CLI de vercel para mirarlo. La medicion anterior, del 2026-08-22 con vercel env ls production, dio cinco nombres ninguno de correo. Un registro fechado superado por otro registro fechado no establece nada sobre el presente, en ninguna de las dos direcciones - ni configurado ni ausente. Lo unico que se afirma hoy es que la premisa vieja "no hay SMTP" quedo superada, no que haya transporte. Lo que si esta verificado por mi mismo contra la fuente: src/lib/email/recipients.ts:98 declara COORDINATION_EMAILS con una direccion viva y :107 la devuelve antes de llegar a la rama de grupo nulo, o sea que el destinatario esta decidido y la afirmacion de que devuelve sourceUndecided incondicionalmente es falsa. La pata de esquema sigue resuelta (previousEmail aprobado 2026-08-23 por db-enamel-cc; emailCanonical es generada y no escribible). POR QUE SIGUE BLOQUEADA: porque el camino de correo NUNCA se ejercito end to end - eso es TERRA-59 - mas la mitad de diseño del flujo de onboarding, que no depende de correo. No por falta de credencial. BORRO LA ULTIMA ORACION DE LA VERSION ANTERIOR porque quedo falsa: decia que la premisa stale sobrevivia en comentarios de fuente en recipients.ts:14-19 y app/administrar/page.tsx:209-217. Los dos se corrigieron en d988f9a y lo verifique en origin hoy: ambos encabezados ahora nombran las dos mediciones con fecha y dicen explicitamente que no afirman el estado actual del transporte.
  • wi-cli-venus blocked · 6w ago
    TERCERA CORRECCION, 2026-09-01 07:24 UTC, Y ES LA QUE IMPORTA PORQUE EL ERROR ERA MIO Y ERA DEL MISMO TIPO QUE VENGO CORRIGIENDO EN OTRAS FILAS. QUE ESCRIBI MAL HACE VEINTE MINUTOS: que el estado del transporte en produccion no se podia medir hoy, textual "nadie volvio a mirar esas variables desde el 2026-08-23 y en este host no hay CLI de vercel para mirarlas". Falso. Mire el instrumento que tenia en la cabeza - el CLI de vercel - en vez de preguntarme que cosa OBSERVA el sistema del que estaba hablando. La app publica un endpoint de salud y ese endpoint lee la configuracion de correo del entorno vivo. MEDICION FRESCA, TOMADA POR MI, no citada: GET https://terra-enamel.pensanta.com/api/health a las 2026-09-01T07:24:41Z devuelve version 0.31.28, deploymentId dpl_3wKBsb5EuVjMavfuCic4d6BNGSEL, dbOk true, mailOk true, mailError null, mailFromName true. Es el deploy corriendo AHORA, no un registro de fuente ni una medicion de hace nueve dias. QUE PRUEBA EXACTAMENTE, y lo acoto porque el instrumento tiene alcance y no decirlo seria repetir el error en la otra direccion: mailOk sale de readMailConfig (src/lib/email/transport-core.ts), que LEE LAS VARIABLES DE ENTORNO y valida su forma - credenciales presentes, host, puerto, direccion de origen valida - y devuelve un enum cerrado de codigos cuando falta algo. NO ABRE UNA CONEXION SMTP Y NO MANDA NADA. O sea que lo que queda establecido es que el entorno de produccion HOY tiene SMTP_USER y SMTP_PASS instalados y con forma valida. No queda establecido que un mail salga. ENTONCES LA PREMISA VIEJA - "no hay SMTP en produccion" - esta MUERTA, y ya no por un registro de fuente superando a otro registro de fuente, sino por una medicion en vivo del deploy actual. Eso es mas fuerte que lo que yo habia dejado escrito y corresponde decirlo asi. LO QUE NO CAMBIA, Y ES POR LO QUE ESTA FILA SIGUE BLOQUEADA: configuracion presente no es entrega funcionando. El camino de correo NUNCA se ejercito end to end porque no existe una cuenta terra sin rol que pueda presentar una solicitud - eso es TERRA-59 y ese sigue siendo el bloqueo real - mas la mitad de diseño del flujo de onboarding, que no depende de correo en absoluto. VERIFICADO POR MI CONTRA LA FUENTE, sin cambio: src/lib/email/recipients.ts:98 declara COORDINATION_EMAILS con una direccion viva y :107 la devuelve antes de llegar a la rama de grupo nulo, o sea que el destinatario esta decidido. La pata de esquema sigue resuelta (previousEmail aprobado 2026-08-23 por db-enamel-cc; emailCanonical es generada y no escribible). LA REGLA QUE ME LLEVO Y QUE VALE MAS QUE ESTA FILA: "no tengo con que medirlo" casi siempre significa "no tengo con que medirlo CON LA HERRAMIENTA QUE PENSE PRIMERO". Antes de declarar algo no medible hay que preguntar que instrumento observa ese sistema, no que instrumento tengo a mano. Es la misma falla que me hizo medir Supabase para una pregunta de backups cuyo backup vive en otro host. BORRE DE LA VERSION ANTERIOR la oracion que decia que la premisa stale sobrevivia en comentarios de fuente en recipients.ts:14-19 y app/administrar/page.tsx:209-217: los dos se corrigieron en d988f9a y lo verifique en origin.
  • wi-cli-venus descriptionChanged · 6w ago
    was: Elazar, 2026-08-23 CLI, verbatim intent: users whose address is not a Google-auth address get an email asking them to supply a UBA or Gmail address; this belongs to onboarding, user management AND the access-request path. His explicit constraint, and it is the part most likely to be got wrong: "dont make the mistake of keeping old non-google auth gmail as main or display or whatever and adding gmail on top of it, gmail or .edu.ar REPLACES old email and old email moves to secondary email field." MEASURED LIVE 2026-08-23 (read-only), because the size of this decides its shape: 159 terra users, and only 4 are linked to GoTrue at all (supabaseAuthId NOT NULL). gmail.com 113 (1 linked) · odontologia.uba.ar 7 (2 linked) · cbc.uba.ar 1 hotmail.com 28 · yahoo.com.ar 3 · outlook.com 2 · hotmail.com.ar 1 institutoferrari.com 1 · um.edu.ar 1 · clinicabraverman.com 1 · pensanta.com 1 (linked) So 34 users sit on consumer domains that are NOT Google-native, plus 3 on custom domains of unknown provider. That is the population this WI exists for, and it is ~23% of the roster - not an edge case. DO NOT ENCODE "NOT GMAIL = CANNOT SIGN IN". IT IS FALSE. A Google Account can be created with ANY address as its username, hotmail included, so some of those 34 may already sign in perfectly. Terra cannot tell which from its own data - all 34 have supabaseAuthId NULL and lastSignInAt NULL, and lastSignInAt is never written anyway (TERRA-80), so BOTH columns are silent for the same person whether they are able to sign in or not. There is no Google API that answers "is this address a Google Account" for a third party. THE ONLY TEST IS AN ACTUAL SIGN-IN ATTEMPT. Design the flow so the address is a HINT that triggers an offer, never a verdict that blocks a login - a user wrongly classified as unable to sign in, and then blocked, is a worse outcome than the one being fixed. SCHEMA: THE COLUMN DOES NOT EXIST AND THE TABLE IS NOT OURS. public.users today holds id, supabaseAuthId, email, emailCanonical, legajo, documentoType, documentoNumber, nombre, apellido, telefono, isSynthetic, syntheticSessionId, createdAt, updatedAt, deletedAt, lastSignInAt, profilePictureRef. There is NO secondary/previous email column. users is SHARED WITH ENAMEL, so adding one is a change to every writer of that table and needs db-enamel-cc GO before any DDL - it is not a terra-local migration. Also note emailCanonical exists and the swap must keep it consistent with email, or the canonical index starts disagreeing with the address people actually use. THE SWAP IS THE RISKY OPERATION, not the email. Replacing the primary address on a row that other things key on is exactly where a half-applied change hides: emailCanonical, the enamel co-tenant's own reads, any access-request ticket already filed under the old address, and the GoTrue identity if one is ever linked. The old address must land in the secondary field in the SAME transaction that rewrites the primary, never as a second step - and the archive/GUC contracts apply because this is an UPDATE on a shared table that erases the previous value (TERRA-28: an UPDATE overwrites with zero trace unless the archive path is armed). COPY, to be reviewed rather than shipped as drafted here - Elazar's own sentence was "Por favor enviar una direcion de uba.edu o gmail para poder usar la app" and he asked for it properly worded. Note "uba.edu" is not the real domain; the live ones are odontologia.uba.ar and cbc.uba.ar. Draft: Asunto: Necesitamos otra dirección de correo para tu acceso a Terra Terra usa el inicio de sesión de Google. Para poder ingresar necesitamos una dirección de Google: tu cuenta institucional de la UBA (@odontologia.uba.ar) o una cuenta de Gmail. Respondé este correo indicando la dirección que vas a usar. Esa pasa a ser tu dirección principal en Terra y conservamos la actual como alternativa. Si ya podés ingresar con la dirección que tenés, avisanos y no cambiamos nada. That last line exists BECAUSE of the false-negative risk above. THREE SURFACES, one rule, and they are named separately because they are three different code paths: onboarding (a user created by the cátedra who has never signed in), user management (administrar/usuarios - an admin looking at a row should SEE that the address is not Google-native and be able to trigger the ask), and the access-request path (/sin-acceso - someone asking for access from an address that will not work should be told so at that moment, not later by email). PARRAFO CORREGIDO 2026-09-01. Lo que decia aca, y que quedo tres dias contradiciendo la propia razon de bloqueo de esta misma fila: "BLOCKED ON TERRA-59. This is a mail-sending feature and terra cannot send mail: no SMTP_USER/SMTP_PASS in the production environment, measured 2026-08-23. Whatever is built here is inert until those two variables land." No lo borro: esa medicion existio, fue del 2026-08-22 contra vercel env ls production, y dio cinco nombres ninguno de correo. Borrarla destruiria lo unico que la hacia auditable, que es que alguien midio y anoto cuando. QUE LA SUPERA: el registro de fuente del 2026-08-23 en src/lib/access-request-actions.ts, donde las dos variables figuran instaladas. La razon de bloqueo de esta fila ya lo decia desde el 2026-08-29 y la descripcion siguio diciendo lo contrario debajo. Una fila que se contradice a si misma se resuelve por el lado que tiene mas texto, y ese era el parrafo viejo. LO QUE NO AFIRMO, y es la unica forma honesta de escribirlo: que el transporte este configurado HOY. Un comentario de codigo es el REGISTRO de una medicion, no la medicion. Nadie volvio a mirar esas variables desde el 2026-08-23 y en este host no hay CLI de vercel para mirarlas. O sea que un registro fechado superado por otro registro fechado no establece nada sobre el presente. POR QUE SIGUE BLOQUEADA IGUAL, con la razon corregida: no por falta de credencial sino porque el camino de correo NUNCA se ejercito end to end - eso es TERRA-59, y ahi se ve que la cola de pedidos si tiene una direccion viva desde el 2026-08-22. Mas la mitad de diseño del flujo de onboarding, que no depende de correo en absoluto.
  • wi-cli-venus assigned · 5w ago
    coder-terra-cc
  • wi-cli-venus assigned · 5w ago
    coder02-terra-ca
  • wi-cli-venus blocked · 5w ago
    Esquema previousEmail y transporte/outbox estan disponibles, pero falta definir y construir el flujo unico en onboarding, administracion y /sin-acceso sin asumir que un dominio no-Google no puede autenticarse. La identidad Google real solo se confirma mediante intento de ingreso; tambien queda sin ejercer la entrega real de correo de TERRA-59.
  • wi-cli-venus titleChanged · 5w ago
    Non-Google-address onboarding requires actual Google sign-in verification and a unified replacement flow
  • wi-cli-venus note · 5w ago
    Independent audit BLOCK 2026-09-10 was limited to a stale categorical title that contradicted the description and blocker. Retitled to the actual authentication-verification risk; final delta review pending.
  • wi-cli-venus verified · 5w ago
    Independent audit PASS 2026-09-10: corrected title is accurate and consistent with the unchanged description and blockedReason; it treats sign-in as an exercised verification requirement, not a non-Google-domain impossibility.
  • wi-cli-venus assigned · 5w ago
    coder-terra-cc
  • wi-cli-venus verified · 5w ago
    CATALOG LOOKUP - db-terra-cc 2026-09-11, marker PREVEMAIL111-mtws26. The blockedReason says 'Esquema previousEmail y transporte/outbox estan disponibles'. Measured: NO previousEmail column exists on public.users, but a prior-address carrier DOES exist under another name and it is empty. The precondition is HALF-BUILT, which is neither branch the row anticipated. (1) public.users full catalog, 17 columns: id uuid NOT NULL default gen_random_uuid; supabaseAuthId uuid NULL; email text NOT NULL; emailCanonical text NULL; legajo text NULL; documentoType text NULL; documentoNumber text NULL; nombre text NOT NULL; apellido text NOT NULL; telefono text NULL; isSynthetic boolean NOT NULL default false; syntheticSessionId text NULL; createdAt/updatedAt timestamptz NOT NULL default now; deletedAt timestamptz NULL; lastSignInAt timestamptz NULL; profilePictureRef text NULL. No column holds a replaced address. (2) Searched the whole public schema for any column ILIKE previous/prior/former/oldEmail, not just the name asked about: one hit, terraPacienteAntecedenteChanges.previousDetail, unrelated. (3) public."userEmails" EXISTS - ordinary table, columns id, userId, email, emailCanonical, fromAt, toAt, createdAt. A temporal address-history table; a row with toAt NOT NULL is a superseded address. So the blockedReason is loosely worded rather than fabricated - the storage for address history is real, as a table under a different name rather than a column. (4) IT IS EMPTY: total_rows 0, superseded_rows 0, distinct_users 0, earliest and latest NULL. Zero rows against 159 users. Nothing has ever written it. (5) Nothing writes it at the DB level: public.users carries exactly two non-internal triggers, tg_users_closeGroupsOnDelete (AFTER UPDATE WHEN deletedAt goes non-null) and trg_users_setUpdatedAt (BEFORE UPDATE), neither touching userEmails. An email change on users therefore leaves no history anywhere, by construction. LIVE INSTANCE OF THE GAP, not hypothetical: under TERRA-210 on 2026-09-10 this lane overwrote two real client-supplied addresses on users and they were preserved only in agents/db-terra/evidence/2026-09-10-terra_210-galvan-bindings.result.md, because the database kept nothing. WHAT IS ACTUALLY OWED: not a db-enamel-cc column request - the table already exists. The write path is owed, at every layer, plus a decision on whether that write belongs to enamel (userEmails is an enamel-owned relation) or may be done by terra alongside its own email writes. Smaller than a schema change. NOT COVERED: I checked triggers on users and the table's contents, which shows nothing HAS written it; I did not sweep functions/views elsewhere that COULD write it.
  • wi-cli-venus audit-finding · 5w ago
    PRECONDITION IS HALF-BUILT, NOT AVAILABLE AND NOT FABRICATED. Marker: UEX-111-20260911. This row's blockedReason says 'Esquema previousEmail y transporte/outbox estan disponibles'. Measured 2026-09-11 by db-terra-cc at coder-terra-cc's request, full catalog rows: THERE IS NO previousEmail COLUMN on public.users (17 columns, none holding a replaced address), and an ILIKE sweep for previous/prior/former/oldEmail across the whole public schema returns one unrelated hit. A repo-wide command grep across ts/tsx/sql/json/md also returns zero. But the field is not false either, and this is the finding. public."userEmails" EXISTS - id, userId, email, emailCanonical, fromAt, toAt, createdAt - which is a temporal address-history table, exactly the shape a replaced address lives in. So the storage half is built, under a different name and as a table rather than a column. The text is loosely worded, not invented. AND IT IS EMPTY, WITH NO WRITER. Zero rows against 159 users. The two non-internal triggers on users touch it not at all. On 2026-09-10 under TERRA-210 two real client addresses were overwritten on users and went nowhere; they survive only in a hand-written evidence file. CONSEQUENCE FOR THIS ROW: the third branch. Neither 'precondition met, proceed' nor 'precondition fabricated, re-raise a column request' - the storage exists and the write path does not, at any layer. That half-built state is invisible from the blockedReason, which is the reusable part: a precondition asserted as AVAILABLE can be partly true, and the partly-true case is the one no audit criterion catches. SPLIT OUT AS TERRA-215 (p1, db-terra-cc, blocked). That row carries the write path and its ownership question for db-enamel-cc; it is blocked on a different person than this row and would survive this row being cancelled. TERRA-111 stays blocked on Elazar's design decision for the replacement flow, unchanged. blockedReason DELIBERATELY NOT EDITED. Correcting it means re-blocking, and on TERRA-143 today an unblocked event was found to have written itself while mutating neither status nor blockedReason - re-block is the operation whose behaviour is currently in doubt. The correction lives here instead.
feature
7w ago by wi-cli-venus
5w ago