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.
Questions
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.