mars
MARS-118
Audit PTD render-GET tooling: enable exercising authed routes (dedicated test-user + stored session, OR a server-side render-self-check route) — Mars leg of the cross-project gap that let Pluto's /informes 500 ship unexercised (audit unauth render-GET -> 302/login, authed routes never actually rendered in PTD)
Blocked normal
cmcoder02-mars-cc
⛔ The RENDER half has no implementation that can exist under the current gate, and that is a standing condition rather than a queue position - so this is blocked, not deferred (deferred would clear blockedReason and the row would read as merely unscheduled).
A credential-less caller cannot render ANY protected page BY CONSTRUCTION: getSession is cookie-bound (src/lib/auth.ts:193, React cache(), await cookies() at :203) and (protected)/layout.tsx:25-28 redirects a null session to /login. The only implementations that reach render are (C) a stored test-user session - rejected by the 2026-06-14 3-PM steer as a standing credential - or (B) the actor-param refactor, filed as MARS-721 and deliberately NOT scheduled. Teaching getSession to accept a non-cookie identity is a session-minting bypass on the gate EVO-68 hardened and is off the table.
UNBLOCK CONDITION: MARS-721 lands, or Elazar overrides the 3-PM steer on the stored session.
The buildable part is MARS-720 (leg A, query-layer only). MARS-720 CLOSING DOES NOT UNBLOCK THIS ROW - A covers no rendering and no session-reading path, and that bound is written into MARS-720's own description precisely so a future reader does not resolve this parent off it.
Sub-tickets — 1/2 done
MARS-721 Backlog low
MARS-720 Done
cmcoder02-mars-cc
Activity
-
Design steer (3-PM + apiimages security caveat): PREFER an in-process server-side render-self-check route (credential-less) over a stored test-user session — a stored session is a standing credential/attack surface. Cross-project umbrella WI on venus side + nw-whey co-owns infra leg; sequenced after live incidents + Pluto P3 close.
-
coder02-mars-cc
-
VERIFIED STILL OPEN 2026-09-11. No render-self-check route exists: src/app/api holds only cron, health, image, internal, log-error, log-ux-event, profile-image. The 3-PM design steer on this row (prefer an in-process credential-less server-side render-self-check over a stored test-user session) is unimplemented. Adjacent surface that did NOT exist when this row was written and changes the build: /api/internal/support-action already establishes the bearer + X-Acting-Agent + idempotencyKey pattern for a sanctioned internal route, so the self-check route has a house shape to follow rather than inventing an auth model.
-
INVESTIGATED 2026-09-11 (coder02-mars-cc). THE STEERED DESIGN CANNOT DO WHAT THE ROW TITLE ASKS, AND THE REASON IS THE AUTH GATE WORKING CORRECTLY - not a gap to engineer around. Nothing built. A CREDENTIAL-LESS CALLER CANNOT RENDER ANY PROTECTED PAGE, BY CONSTRUCTION. getSession is cookie-bound: src/lib/auth.ts:193 wraps it in React cache() and it reads await cookies() at :203 through createServerClient. src/app/(protected)/layout.tsx:25-28 calls getSession() and redirect(\"/login\") when it is null. So every route under (protected) is gated at the layout, and an internal bearer route - which carries no user cookie - cannot produce a session for any of them. The 3-PM steer ('prefer an in-process credential-less server-side render-self-check over a stored test-user session') is sound about the credential, and the thing it prefers does not exist as a RENDER check. Any implementation that made it work would have to teach getSession to accept a non-cookie identity, which is a session-minting bypass in prod code on the exact gate EVO-68 hardened - a far worse outcome than the uncovered render it fixes. WHAT IS ACHIEVABLE CREDENTIAL-LESS, AND ITS EXACT COVERAGE BOUND. The Pluto /informes 500 that motivated this row was a DATA-LAYER failure surfacing during render, not a rendering failure, and the data layer is partly reachable without a session because many fetchers take explicit ids: getUsuarioBasic(id), getUsuarioFullExtras(id), getUsuarioAssignmentsEditable(id), getClinicalStats(...) and their siblings. Those can be exercised from a bearer-authed internal route. What CANNOT: 12 of the 46 page.tsx files under (protected) call getSession() in the page itself, and several lib modules call it internally (administrar-actions.ts, self-service-assignment.ts, data-health-actions.ts, postgrado-actions.ts, estadisticas-email-actions.ts, rectificar-toast-actions.ts, edit-rules-ack-action.ts, profile-photo-request-action.ts among others) - so any page whose reads go through those is out of reach. /alumnos is one: its page imports from administrar-actions. THREE HONEST OPTIONS, none of which closes the row's whole class: A. Data-layer self-check route, bearer-authed, mirroring /api/internal/support-action's shape (Authorization: Bearer, X-Acting-Agent forensic-only, the bearer the only credential). An ALLOWLIST IN CODE of (name -> id-parameterised fetcher) pairs, never a fetcher named by the request, and the response carries ONLY {name, ok, ms, errorName, errorMessage} - never row data, so it cannot become an exfil endpoint or a lateral read. Representative ids resolved server-side by role rather than taken from the request, so there is no impersonation-shaped input at all. B. Refactor the session-reading libs to take an explicit actor parameter instead of calling getSession() internally. Large, but it is the correct architecture independently: a lib function that reads cookies cannot be exercised by anything except a browser, which is the root cause of this row rather than a symptom of it. C. The stored test-user session. Rejected by the 3-PM steer as a standing credential and I agree; it is also the only option that covers RENDER. MY RECOMMENDATION: A now, and LABELLED HONESTLY - a query-layer control that does not cover rendering, does not cover any session-reading path, and therefore does NOT close the Pluto-incident class, only the part of it where a query throws. Commons is explicit that absence of exercise is not verification and that a control never seen to fail is a green line rather than a control, so the acceptance criterion for A must include forcing a deliberate failure through it before it is trusted. B is the real fix and belongs in its own row. Whoever writes A must not report it as closing this one. STILL CLASS-A: a new internal bearer route is an auth surface, so this takes the pre-implementation design ping before any code, and the allowlist-plus-no-row-data shape is the part that needs review, not the plumbing. Coverage: read auth.ts's getSession and its cookie read, (protected)/layout.tsx's gate, /api/internal/support-action's auth header, the export signatures in usuarios.ts and estadisticas-clinico.ts, and counted getSession() call sites across (protected) page files and src/lib. NOT exercised: no route built, no build inspected, no attempt to call any fetcher outside a request scope.
-
SCOPE CALL (coder-mars-cc, PM seat). BUILD A, CONDITIONALLY. Reject B as a substitute and reject 'neither'. WHY THE CALCULUS MOVED, and it moved against my own first position. I was going to reject A and take B (refactor session-reading libs to an explicit actor param) on the grounds that B makes the data layer vitest-exercisable with no route needed. THAT ARGUMENT IS FALSE HERE and I withdraw it. Mars has no local Postgres (evolutiva-commons.md SS-Database: production Supabase only, restoring a dump to test against is disallowed); 9 test files vi.mock("@/lib/db"); and .gitpush-pre.sh's typegen step is 'pnpm exec next typegen' = Next route/PageProps types, NOT 'supabase gen types' - grep for supabase-gen-types|generate_typescript_types|db:types|Database[ over package.json+scripts/ returns ZERO. So vitest tests CODE PATHS, never SQL validity, and nothing at push time reads the live schema either. The motivating incident (Pluto /informes 500) was real SQL failing against the real schema at runtime. ONLY SOMETHING EXECUTING THE REAL QUERY AGAINST THE REAL DB REACHES THAT CLASS. B is the right architecture and is irrelevant to this row's failure mode. A's COST IS ALSO LOWER THAN 'a new auth surface' IMPLIES. POST /api/internal/support-action already establishes the bearer + X-Acting-Agent shape on this app; A adds one more route in an existing house shape, and its response carries NO ROW DATA - only {name, ok, ms, errorName}. There is no exfil path to price. GATING CONDITION, NOT A NICE-TO-HAVE: A SHIPS ONLY IF THE ALLOWLIST CANNOT SILENTLY SHRINK. A hand-maintained (name -> fetcher) map covers what someone remembered to add; a fetcher added next month is absent and NOTHING FAILS - coverage decays while the route reads green, which is 'a green line, not a control'. So A must include an enforcement test (or lint) that enumerates the exported id-parameterised fetchers in the covered modules and FAILS when one is not registered in the allowlist. Adding a fetcher without registering it becomes a red build. If that enforcement cannot be built, A IS DECORATIVE AND THE ANSWER IS NO ROUTE - say so rather than shipping the map alone. ACCEPTANCE, sharpened: 'force a deliberate failure through it before trusting it' means THE INCIDENT SHAPE, not a generic throw. Point one covered fetcher at a column that does not exist, confirm the route reports ok:false with the SQL errorName, then revert. Proving the route reports exceptions is NOT proving it detects schema drift. LABELLING, binding: A is a QUERY-LAYER control. It does not cover rendering, does not cover any session-reading path, and DOES NOT CLOSE THE PLUTO-INCIDENT CLASS - only the part where a covered query throws. coder02-mars-cc wrote that bound itself and it is adopted verbatim. Whoever builds A must not report it as closing this row. ROW DISPOSITION: MARS-118 does NOT close on A. The render half has no credential-less implementation - coder02 proved a render self-check cannot exist without a session-minting bypass on the gate EVO-68 hardened, and getSession is cookie-bound (src/lib/auth.ts:193, React cache(), await cookies() at :203) with (protected)/layout.tsx:25-28 redirecting null sessions, so a credential-less caller cannot render any protected page BY CONSTRUCTION. That is a real standing condition, so this row goes BLOCKED, not deferred (deferred would clear blockedReason). Condition: render coverage needs B or a stored test-user session; the 3-PM steer rejects the latter and B is not scheduled. NOT AN OVERRIDE OF THE 2026-06-14 3-PM STEER. Its operative concern is NO STANDING CREDENTIAL FOR A TEST USER; A honours that (a bearer for an internal ops route is not a user session). The steer was silent on what to do when its preferred shape cannot exist. This decides in that space. B gets its own row, filed now, NOT built this week - large, no incident driving it, and no-speculative-work is in force. SEQUENCING for coder02-mars-cc, who now holds four: 1) MARS-553 (GO already given, smallest, deletes code), 2) MARS-213 column spec (write + DM db-mars-cc, no code), 3) MARS-118 A (this decision), 4) MARS-147 (db-mars-cc DDL leads; coder02 follows the DDL).
-
The RENDER half has no implementation that can exist under the current gate, and that is a standing condition rather than a queue position - so this is blocked, not deferred (deferred would clear blockedReason and the row would read as merely unscheduled). A credential-less caller cannot render ANY protected page BY CONSTRUCTION: getSession is cookie-bound (src/lib/auth.ts:193, React cache(), await cookies() at :203) and (protected)/layout.tsx:25-28 redirects a null session to /login. The only implementations that reach render are (C) a stored test-user session - rejected by the 2026-06-14 3-PM steer as a standing credential - or (B) the actor-param refactor, filed as MARS-721 and deliberately NOT scheduled. Teaching getSession to accept a non-cookie identity is a session-minting bypass on the gate EVO-68 hardened and is off the table. UNBLOCK CONDITION: MARS-721 lands, or Elazar overrides the 3-PM steer on the stored session. The buildable part is MARS-720 (leg A, query-layer only). MARS-720 CLOSING DOES NOT UNBLOCK THIS ROW - A covers no rendering and no session-reading path, and that bound is written into MARS-720's own description precisely so a future reader does not resolve this parent off it.
-
DEFERRED TO AFTER THE CAP RESETS FRIDAY. pmmaster-evolutiva-cc, GO on Elazar's behalf (standing grant), 2026-09-11. Neither unblock door opens this week. The row STAYS BLOCKED with the condition already recorded - that is the honest state, and MARS-720 must not close it. DOOR 1 - overruling the 2026-06-14 3-PM steer to allow a stored test-user session: REFUSED, AND NOT LATER EITHER. It trades a permanent standing credential plus a session-minting path through the exact gate EVO-68 hardened, in exchange for test coverage. Wrong direction on a security control. pmmaster's framing, worth preserving because it is the general rule and not a MARS-118 fact: the fact that it would be CONVENIENT is what makes it worth refusing in writing. Treat this door as CLOSED unless Elazar personally reopens it - do not re-raise it. DOOR 2 - scheduling MARS-721 (the actor-param refactor): right shape, wrong week. It is the clean fix and it should happen. But the fleet is at 7d:55% with 6.0 days to run (9.2%/day) and Elazar's stated priority is getting TERRA and MOVILBA working for users. Render-half test coverage on a Mars endpoint is a nice-to-have against a broken login on another project. MARS-721 stays filed and UNSCHEDULED; Friday-or-later. So the unblock condition is unchanged and now has a date attached rather than an owner: MARS-721 lands (not before Friday), or Elazar personally reopens door 1. Nothing further is needed from this lane until then.
coder
2026-06-14 by wi-cli-venus
5w ago