Story-1455 pramaan-admin EPIC-1400 trace graph migration (Phase-2) Roster R7c

One id for the request, not two

The founder decision of 2026-06-19 retired the homegrown correlation_id and standardized on the W3C trace_id as the single technical request identity. Story-1453 (functions, LOCKSTEP) renamed the published error field; this is its admin consumer. The admin surface stops minting a correlation id, reads trace_id off error responses, carries trace_id on every support report, and regenerates the contract — so a staff member's reference and X-Ray's trace are finally the same string.

What changed

The cid is gone end to end on the admin side. src/proxy.ts no longer mints or stamps x-pramaan-correlation-id / the legacy x-correlation-id mirror; the W3C traceparent it already resolves is now the only request identity it forwards. src/lib/gateway.ts parses the new ErrorResponse.trace_id off domain errors (the generated contract, refreshed from functions main) and exposes it as GatewayError.traceId. Every consumer that used to read or carry a correlation id — diagnostics, the support reference, bug-report, client-error, and the business action log — now reads and carries trace_id.

proxy drops the cid mint

src/lib/correlation.ts is deleted; REQUEST_ROUTE_HEADER moves to src/lib/request-headers.ts. Proxy logs carry trace_id/span_id only. The Bug-1473 active-span traceparent injection in gateway.ts is untouched, so the connected X-Ray graph still holds.

error responses carry trace_id

openapi/admin-api.json refreshed from functions main and src/gen regenerated: ErrorResponse.correlation_id → required trace_id. GatewayError.fromResponse reads parsed.data.trace_id. HealthResponse.correlation_id is a separate, unchanged backend field and is left alone.

support can find the request

The bug-report and client-error payloads now carry trace_id (from window.__pramaan.traceId / the trace_id meta), so support can paste it straight into X-Ray. The page reference component (TraceReference) and banner (TraceBanner) display the trace_id.

The #1473 graph is not regressed

The whole point of the migration is one connected trace, so the riskiest thing this Story could do is break the admin equivalent of Bug-1471 — the single-source traceparent injection that keeps the Vercel span and the backend span on the same X-Ray trace id. That path lives in src/lib/gateway.ts: when a live @vercel/otel span is active, the gateway injects the active context with the registered W3C propagator (propagation.inject) so the outbound carrier always matches the exported span. That block is left exactly as Bug-1473 shipped it. The cid rip-out only removed the dead correlation-header forwarding around it; the active-span traceparent remains the sole, untouched source of trace identity on the serverless leg.

Contract and queries, kept honest

The generated client is canonical, never hand-edited: openapi/admin-api.json was refreshed from the merged functions OpenAPI and pnpm kubb:generate reran, producing a deterministic 37-file diff that is entirely the ErrorResponse rename plus the error-schema description strings that embed it. The observability runbooks followed the code: docs/obs/QUERIES.md CloudWatch Logs Insights and jq queries, docs/obs/STRATEGY.md, docs/obs/LOG_SHAPE_V1.md, and docs/obs/log-shape.schema.json all key on trace_id (32-hex, nullable under the kill switch) now, and the propagation header is traceparent, not X-Correlation-Id.

Verification proof

Gates green: pnpm exec tsc --noEmit (exit 0), pnpm run lint (0 errors; 7 pre-existing warnings in unrelated files), pnpm run build (compiled successfully, all 18 routes), pnpm run test (518 passed across 86 files), and pnpm run test:storybook (425 passed across 120 files). git diff --check clean.

Contract freshness: a fresh pnpm kubb:generate from the committed openapi/admin-api.json produces a byte-identical src/gen — codegen-drift clean. No file under src/gen was hand-edited.

trace_id resolves: bug-report and client-error payloads now ship trace_id; proxy / gateway / action-log lines emit trace_id + span_id matching the W3C value, so a CloudWatch filter trace_id = "…" and an X-Ray trace lookup land on the same request.

Graph intact: the Bug-1473 active-span propagation.inject path in gateway.ts is unchanged; new regression-guard tests assert proxy and gateway send no correlation headers while the traceparent keeps flowing.

Follow-up noted: the AGENTS.md "Observability bridge" section still describes the old cid stamping; its canonical source is pramaan-docs ADR-104/ADR-124, updated under the EPIC, so the rulebook mirror is left for a docs chore rather than ridden on this feature PR.