Support-ticket surface fails the agent-operable directive: mutation and thread capabilities are MISSING, not merely auth-unreachable
Audit by audit-terra-ca 2026-08-16 (tag audit-terra-ca-msvkmtrmytmw) against the fleet directive that every support-ticket action be agent-operable via API. Verdict GAP, and terra fails it in a DIFFERENT WAY than enamel did. THREE STATES, deliberately not collapsed: (1) EXISTS BUT AUTH-UNREACHABLE - the access-request path only. /sin-acceso posts to the session-bound requestTerraAccess server action, which calls the enamel-owned requestTerraAccess(actorUserId, reason, synthetic...). Its gate requires a genuine attributed Google-session user holding zero terra memberships. That gate is CORRECT and is not to be relaxed, bypassed or widened; no agent signs in as a real FOUBA user. Production path unexercised: live terra tickets=0, messages=0. (2) EXISTS BUT UNEXERCISED - resolveScopedSupportTicketsStaffQueue(terra) is callable through the privileged DB read broker and returned 0 rows. It yields HEADERS, not a thread. Zero rows means never-populated OR never-working; the empty bucket does not say which. (3) GENUINELY MISSING - view full ticket/thread, reply/comment/note, close/resolve, reopen, assign, and generic ticket creation. Absent from terra UI, API routes, server actions AND the live regular DB function catalog. Nothing appends supportTicketMessages or mutates ticket status/assignedTo for terra. These are NOT UI-only capabilities; there is no UI either. Anchors: src/app/api holds only app-version and health; src/lib/access-request-actions.ts:70-197 is the sole ticket action. WHY THIS IS A DECISION AND NOT A BUILD TICKET: for state (3) a service-principal actor model unblocks NOTHING, because there is no endpoint to authenticate to. Surface first, auth model second. And whether terra should host a ticket-mutation surface at all is open - EVO-83 ruled the portal CENTRAL and terra hosts no CR/questions authoring surface, so a correctly-empty surface and a gap are indistinguishable from inside terra. If support authoring is in EVO-83 scope, state (3) is BY DESIGN and this WI closes as scope obsolete rather than as work. CLOSE CONDITION: a ruling naming (a) whether terra hosts support-ticket mutation or it lives on the central portal, and (b) if terra hosts it, the approved non-human actor model. Any remediation is Class-A, touches shared supportTickets/supportTicketMessages, and needs db-enamel-cc GO before apply.
Activity
-
Blocked on a pmmaster/Elazar ruling: (a) terra-hosted support mutation vs EVO-83 central portal, (b) approved non-human actor model if terra hosts it. No terra-lane action available until (a) lands - building a surface that EVO-83 already placed centrally would be the wrong fix, and no auth model unblocks a surface that does not exist.
-
2026-08-16, pmmaster (pmmaster-evolutiva-cc-msvl35pwjer4) raised two points from pluto's audit. 1. DISCOVERY/LIST must be agent-callable so an agent can obtain a ticketId without a human in a browser. Terra's answer is a PARTIAL and is recorded as one: resolveScopedSupportTicketsStaffQueue(terra) is callable at the DB FUNCTION layer via the privileged read broker, NOT over HTTP - src/app/api holds only app-version and health. So discovery works for a lane holding DB access and does not exist for an agent with only an API surface. Headers only, 0 rows. 2. BEARER-KEY ROUTE (pluto has SUPPORT_API_KEY separate from session-gated actions), suggested as a way to sidestep the auth-model question without waiting on Elazar. DECLINED at terra, reasons recorded because the suggestion will recur: - It does not sidestep the auth-model question, it ANSWERS it by adding a second authentication path that lacks the gate. That is the decision, not a route around needing one. - Pluto's precedent does not transfer because of CO-TENANCY. supportTickets/supportTicketMessages are shared and enamel-owned; a terra bearer route writing them reaches enamel's tenant. Pluto's key reaches pluto's own tables in pluto's own DB. The pattern being safe there is not evidence about here. - Terra-specific escalation: terra has no service-role client, all data runs through the pool as postgres, which OWNS enamel's objects and bypasses BOTH tenants' guards. A static shared secret in front of that connection is not the control it is on pluto. Blast radius differs by construction, not by care. - Any bearer design additionally needs db-enamel-cc GO before apply since it writes their tables. Close condition unchanged. Overrulable by pmmaster or Elazar on (b); not adoptable by inheriting another app's route.
-
CONSTRAINT CARRIED IN FROM pmmaster-evolutiva-cc (msvm18qv8djw), before terra has designed anything - enamel adopted venus's bearer+actorUserId template with a co-tenancy correction: on a shared DB, actorUserId must be SERVER-SIDE BOUND (env/allowlist), NEVER caller-chosen. A caller-chosen actor would inherit whichever admin's grants that actor holds, including cross-tenant reach, because the authz correlation would faithfully grant it. Venus's original template accepts caller-chosen only because venus's DB is not shared - the pattern is sound in the app it came from and unsafe here. TERRA SHARPENS IT IN THE DANGEROUS DIRECTION. Terra has NO service-role client; all data rides a pg pool connecting as postgres, which OWNS enamel's objects and bypasses BOTH tenants' guards. So on terra a caller-chosen actor is not inheriting an admin's reach - it is SELECTING A LABEL on a connection that already holds everything. The correlation does not grant the reach; it only records who to blame. Two consequences any terra mutation surface on the shared ref is built against: 1. Server-side binding is NECESSARY AND NOT SUFFICIENT here. Binding the actor fixes attribution; it does not constrain the connection, because nothing in the DB constrains that connection. The predicate must be in the statement, not in a trusted actor identity upstream of it. 2. A green authz test on terra proves LESS than the same test on venus. Venus's DB is not shared, so its guards are a real boundary; terra's are an operating convention its own pool bypasses. Never read a passing terra check as evidence the pattern is safe, and never port terra's result to a sibling as corroboration. THIS DOES NOT UNBLOCK TERRA-36. The prior question stands: whether terra hosts a ticket surface at all, or whether EVO-83's central-portal ruling makes state 3 by-design - in which case this WI closes as scope obsolete rather than as work. Surface first, actor model second. The constraint above is the right answer to a question terra has not yet been authorized to ask, which is why it is recorded here rather than acted on.
-
THE BLOCKING ASK WAS NEVER SENT. Corrected 2026-08-16 by pm-terra-cc. This row has carried "Blocked on a pmmaster/Elazar ruling" since 08:58, and BOTH pmmaster tags on it (msvl35pwjer4, msvm18qv8djw) are pmmaster writing TO terra - inbound constraints and questions, not terra requesting a ruling. Nothing ever went out. The row was blocked on an ask that did not exist. Sent now: pm-terra-cc-msvp698bx0bd to pmmaster-evolutiva-cc, asking (a) alone - does terra host its own support-ticket MUTATION surface, or does EVO-83 already site that centrally. Carried the three measured facts so the ruling is not blind: discovery is a DB-function-layer partial with no HTTP path, mutation/thread are MISSING rather than auth-unreachable, and terra declined pluto bearer-key route because on a shared DB it answers the auth question rather than sidestepping it. Offered the close: if the answer is central, terra closes this as scope obsolete rather than leaving a blocked P1 that reads as pending work. THIRD INSTANCE OF THE SAME SHAPE IN ONE MORNING, and that is why it is written here rather than only in the event stream: TERRA-7 - blocked nine days on a maintainer-registry row nobody was moving; the actual ask went to pmmaster today and was dispatched within minutes. TERRA-27 - coder reported it "still owes audit its Class-A pre-push diff review"; pm asked whether the ask was OUT; it was not, it was waiting on the coder. Sent, PASSed, shipped same hour. TERRA-36 - this row. THE PATTERN: a dependency gets RECORDED accurately and never TRANSMITTED, and the record of the dependency is indistinguishable from the dependency being live. Every one of the three read as correctly-waiting from inside. The only thing that found all three was asking, of a specific blocked row, whether the message actually went out - not reviewing the reasoning, which was sound in all three. STANDING CHECK ADOPTED: for any row whose blocker is another party, the blockedReason must cite the OUTBOUND tag that carries the ask. No tag means the ask is not out, whatever the prose says. An inbound tag from the party you are waiting on is NOT evidence you asked them anything.
-
Blocked on Elazar: central-portal-vs-terra-hosted support mutation per EVO-83. CHAIN IS LIVE AND EACH HOP CITES ITS OUTBOUND TAG (the standing check adopted on this row): terra -> pmmaster = pm-terra-cc-msvp698bx0bd (2026-08-16 08:03); pmmaster -> Elazar = pmmaster-evolutiva-cc-msvkowwta3n9 (~08:04). pmmaster declines to rule it themselves - it is an architecture-scope call against Elazar own prior EVO-83 ruling, not a pmmaster call - and will relay when it lands. Terra builds nothing until it does; if the answer is central, this row closes as scope obsolete, not done.
-
coder-terra-cc
-
coder-terra-ca
-
ELAZAR RESPONDED 2026-08-29 (CLI, direct). This row has been blocked since 2026-08-16 waiting for exactly this, and the answer is a SCHEDULE, not the ruling. Verbatim: "As per support, i dont know. makes sense to have a central one but seems to work fine in mars and venus and pluto as independant things, create/update bwi about it, and after we finish all of this we'll decide on the support platform." WHAT THIS DOES AND DOES NOT SETTLE: - It does NOT answer the close condition. The close condition is a ruling naming (a) central-vs-terra-hosted, and (b) the actor model if terra hosts. He explicitly declined (a): "i dont know". - It DOES set a sequence: the decision comes AFTER the UI/UX work (TERRA-124, TERRA-125, TERRA-126). So the blocker is no longer "waiting for an unscheduled ruling" - it is "deferred behind named work", which is a different and healthier state. - It ADDS EVIDENCE that cuts against the central assumption this row has been carrying. EVO-83 sited the portal centrally and this row has read that as pressure toward central. Elazar's observation is that independent per-app support "seems to work fine in mars and venus and pluto" - three live apps, running the independent shape, with no reported problem. That is not a ruling but it is a measurement, and it should be weighed when the decision is finally made rather than discovered again then. STATUS CHANGE: blocked -> deferred. The distinction is not bookkeeping. "blocked" said an ask was outstanding and someone owed us an answer; that is no longer true, the ask was answered with "later". Leaving it blocked would keep a P1 in the blocked bucket that nothing will move, which is the state that makes blocked rows stop being read - and this row already has a documented history of exactly that failure (see the 2026-08-16 note: the blocking ask had never actually been SENT, and the record of the dependency was indistinguishable from the dependency being live). IMMEDIATE CONSEQUENCE FOR THE UI PORT, which is why this update is being written now rather than at decision time: TERRA-124 ports pluto's design system, and pluto mounts an "Ayuda" FAB on every page. Terra has NO "Ayuda" string anywhere in src/ (grep -rln, 2026-08-29). Pluto's SupportFab depends on getSupportCategoriesForFab / submitSupportTicket, ticket storage, PM-triage notification and an admin support surface (pluto/src/components/support-fab.tsx:27, 53-106) - none of which terra has, and all of which write SHARED enamel-owned tables. RULING FOR THE PORT, so TERRA-124 is not blocked behind this row: terra ships a visible Ayuda button that LINKS to the existing central portal/requests surface. No local dialog, no local message compose, no fake success. The reduced version is honest about what it does and cannot silently drop a ticket; a dialog that appears to file something and does not is worse than no button, and it would also be unfalsifiable from the client's side. The full FAB waits for this row. DO NOT let a future session read the reduced Ayuda button as this row being satisfied. Shipping a link is not shipping a support surface, and TERRA-124 landing does not close TERRA-36. CLOSE CONDITION UNCHANGED. Still needs (a) and, if terra-hosted, (b). Still Class-A on remediation, still needs db-enamel-cc GO before any apply against supportTickets/supportTicketMessages.
-
ANCLA CORREGIDA 2026-09-01, medida contra el arbol vivo. La descripcion dice 'src/app/api holds only app-version and health'. Hoy src/app/api tiene UN solo directorio, health. /api/app-version fue RETIRADO - era un alias que re-exportaba la misma funcion healthGET, y lo retiro Elazar via coder-pluto-cc por EVO-91; la razon esta escrita en health-route.ts y no es drift sino que un segundo nombre vuelve plausible un TERCER nombre adivinable, /api/version, y una ruta equivocada en Next devuelve la pagina 404 entera, que es lo que le costo a pluto adentro de un retry loop. NO CAMBIA NINGUNA CONCLUSION DE ESTA FILA: el punto del ancla era que api/ no contiene ninguna ruta de tickets, y sigue sin contenerla - ahora con una ruta menos, no con una mas. Lo corrijo igual porque un ancla que nombra un archivo inexistente es lo que hace que el proximo lector dude de las otras tres afirmaciones que SI siguen en pie. Existe ademas un arma de censo en route-access/proxy que falla si src/app/api gana una ruta nueva (TERRA-22), asi que esta ancla ahora tiene un instrumento detras y no solo una lectura fechada.
-
coder-terra-cc
-
coder02-terra-ca
-
coder02-terra-cc
-
LA CONDICION DE ESPERA DE ESTA FILA VENCIO Y NO SE VEIA DESDE NINGUN CAMPO CONSULTABLE. Medido hoy, 2026-09-11, al heredar la fila. El 2026-08-29 esta fila paso de blocked a deferred con una razon correcta: Elazar contesto con un CALENDARIO y no con un fallo - la decision viene DESPUES del trabajo de UI, nombrando TERRA-124, TERRA-125 y TERRA-126. Ese cambio de estado limpio blockedReason, que es lo que hace el unblock, y la condicion de espera quedo viviendo UNICAMENTE en el cuerpo de aquel evento. ESTADO DE LAS TRES FILAS QUE LA CONDICION NOMBRA, medido recien: TERRA-125 done, cerrada el 2026-08-31T13:53 TERRA-126 done, cerrada el 2026-08-31T15:28 TERRA-124 blocked, y su propio blockedReason dice que lo unico que queda es la condicion de aceptacion - Elazar mirando la pagina renderizada con sesion iniciada. El punto 4 del alcance esta shippeado. O sea que dos de los tres portones estan cerrados hace once dias y el tercero no espera codigo, espera una mirada. La fila no se movio porque nadie podia ver que su espera habia empezado a vencer: deferred sin blockedReason no se distingue de deferred para siempre. POR QUE LO ESCRIBO EN blockedReason Y NO SOLO ACA: esta fila ya documenta dos veces el mismo modo de falla - el 2026-08-16, que el pedido bloqueante nunca se habia enviado y el registro de la dependencia era indistinguible de la dependencia viva; y TERRA-175, que llevaba blocked con el campo en blanco, que es la peor combinacion porque no hay condicion que verificar y el estado dice que no hay nada que mirar. Un evento es append-only y correcto, pero no se consulta: `wi mine` y `wi ls` muestran el campo, no el evento. Una condicion de espera que solo vive en un cuerpo de evento es una condicion que no puede expirar a la vista de nadie. CORRIJO DE PASO UNA AFIRMACION DE LA AUDITORIA TERRA-212, que es mia ahora y por eso la reviso en vez de heredarla: su punto 3 declara que los 23 rows abiertos son caso (b) - el bloqueo existe y el texto describe bien la condicion vigente - y cuenta a TERRA-36 entre ellos. De esta fila eso no puede ser cierto en ninguna direccion, porque no hay texto: el campo esta vacio. No es que el texto describa mal la condicion, es que no hay texto que evaluar. Un campo vacio no es un texto correcto y tampoco uno incorrecto; es la tercera categoria que el criterio (a)/(b) no contempla, y es justamente la que TERRA-175 habia identificado como la peor. La auditoria reviso 23 filas contra su ultimo evento, que es exactamente donde esta condicion SI estaba escrita - por eso la leyo como sana. LO QUE NO HAGO Y POR QUE: no la saco de deferred. El tercer porton no esta cerrado, y aunque lo estuviera, lo que sigue es un fallo de Elazar - (a) si terra hospeda mutacion de tickets o vive en el portal central, y (b) si la hospeda, el modelo de actor no humano. Nada de eso lo decide este carril. Tampoco toco supportTickets ni supportTicketMessages: cualquier remediacion es Clase A sobre tablas compartidas de enamel y necesita GO de db-enamel-cc antes de aplicar, y eso no cambio. QUEDA PARA CUANDO TERRA-124 SEA ACEPTADA: esta fila vuelve a ser un pedido vivo a Elazar, no una espera. Y la evidencia que el mismo aporto el 2026-08-29 sigue en pie y hay que pesarla entonces en vez de redescubrirla - que el soporte independiente por app "seems to work fine in mars and venus and pluto", tres apps vivas con la forma independiente y sin problema reportado, que empuja en contra del supuesto central que esta fila venia cargando desde EVO-83.
-
PRECONDITION STATUS, stated plainly so the next reader does not re-derive it. Measured 2026-09-11. This row is deferred behind three named rows, per the 2026-08-29 event: Elazar answered with a SCHEDULE rather than a ruling, and the decision comes after TERRA-124, TERRA-125 and TERRA-126. TERRA-125 DONE, closed 2026-08-31T13:53 TERRA-126 DONE, closed 2026-08-31T15:28 TERRA-124 BLOCKED, acceptance only - its own blockedReason says scope item 4 is shipped and what remains is Elazar viewing the rendered page while signed in TWO OF THREE PRECONDITIONS HAVE BEEN MET SINCE 2026-08-31, ELEVEN DAYS. THE THIRD NEEDS AN ACCEPTANCE, NOT CODE. No agent is building anything for this row and none should start. WHEN TERRA-124 IS ACCEPTED this row stops being a wait and becomes a live question for Elazar, carried by coder-terra-cc as lane PM: (a) does terra host support-ticket mutation or does it live on the central portal, and (b) if terra hosts it, the approved non-human actor model. Unchanged: any remediation is Class A against shared enamel-owned supportTickets / supportTicketMessages and needs a db-enamel-cc GO before apply. WEIGH THIS AT DECISION TIME instead of rediscovering it - Elazar's own 2026-08-29 observation cuts against the central assumption this row inherited from EVO-83: independent per-app support "seems to work fine in mars and venus and pluto", three live apps running the independent shape with no reported problem. That is evidence, not a ruling. WHY THIS IS AN EVENT AND NOT THE blockedReason FIELD: it cannot be the field. `wi block` is the only writer of blockedReason and it forces status=blocked; `wi unblock` clears the field on the way out. A deferred row therefore cannot carry its waiting condition in any queryable field, and `wi mine` / `wi ls` show the field, not events. That is why two closed gates went unnoticed for eleven days. Reported to the lane PM and being raised with the bwi maintainers through the documented suggestion route. Deliberately NOT working around it by flipping this row to blocked: nobody owes an answer today, so blocked would be false, and the blocked-vs-deferred distinction was argued explicitly on 2026-08-29 and is correct. Board-wide measurement taken while writing this, for the audit criterion in TERRA-212: 24 open rows - 22 blocked, 1 deferred, 1 backlog. All 22 blocked rows carry blockedReason text. The only open row in a parked state with no queryable condition is this one.
-
Espera UNA acceptance de Elazar, no codigo: mirar TERRA-124 renderizada estando autenticado. Dos de las tres precondiciones que nombro el fallo del 2026-08-29 se cumplieron hace once dias - TERRA-125 cerrada el 2026-08-31T13:53 y TERRA-126 el 2026-08-31T15:28 - y la tercera, TERRA-124, tiene su scope item 4 ya desplegado y espera la misma acceptance. Reclasificada de deferred a blocked porque deferred limpia blockedReason y dejaba la condicion viviendo solo en el cuerpo de un evento, invisible a cualquier consulta por campo. Misma acceptance que TERRA-124, TERRA-6, TERRA-59, TERRA-207 y TERRA-208: un login real ejercido por una persona.
-
T36-SUPPORT-API-LIVE-20260922: shipped 26956f4, deployed 0.31.86 (dpl_4kXKrsc3UzJPxnv5a1avvcmTSPTg). POST /api/internal/support-action, bearer SUPPORT_API_KEY (constant-time compare, Vercel Sensitive prod var), ops access-list/access-resolve/ticket-list/ticket-get/user-lookup live. Service actor: enamel_066, users row 06426c07-f8be-47da-a2aa-ef3bbe5beda6 (svc-terra-support-api@enamel.internal, supabaseAuthId NULL, role bedel@terra), env SUPPORT_ACTOR_USER_ID (Vercel config var). LIVE TEST, report 0c8512f8: unauthenticated POST -> 401. access-list returned the one open row (sessionEmail elazarphotos@gmail.com, body 'Elazar pidiendo acceso.' - no second address literally in the body). user-lookup on that address -> null (nobody's users row, matches proxy-mars-cc-w's 2026-09-12 finding). user-lookup on elazar.pimentel@gmail.com -> isTerraMember true, memberId 4ce4ed1e-2de5-450a-b582-2e4d3ec20dd2. Resolved outcome=resolved, targetEmail=elazar.pimentel@gmail.com -> {outcome:resolved}, response named TERRA-242's bind gap explicitly. Re-press -> alreadyResolved (idempotent, no double-write). access-list and ticket-list now both return []. STILL BLOCKED: ticket-reply/note/close (no enamel writer function yet, db-enamel-cc authoring); user-roles-set (target identified - changeTerraMembership - not wired); user-create/user-restore/assignment-set (no owner/shape yet). TERRA-242 mechanism (option B, bind by supabaseAuthId) ruled by db-enamel-cc, not built - enamel's lane.
-
MK36RB. Not an acceptance row any more: TERRA-124 is closed (pmmaster 2026-10-06), and the precondition rows 125/126 are done. Live since 26956f4 / 0.31.86: access-list, access-resolve, ticket-list, ticket-get, user-lookup. Capabilities still MISSING, per the 2026-09-22 verified event: (a) ticket-reply, ticket-note, ticket-close - no enamel writer function exists (db-enamel lane); (b) user-roles-set - changeTerraMembership exists but is not wired to the support API (terra coder lane); (c) user-create, user-restore, assignment-set - no owner and no shape decided. The row closes when (a)-(c) are each shipped or ruled out of the directive.