Story 1456: FN-RIP — delete the correlation_id machinery (R7d)
Why this exists (the catastrophe it prevents). PRAMAAN ran two request-identity systems side by side: the homegrown correlation_id (cid) — a per-request UUID minted by our own middleware, threaded through a ContextVar, stamped on every log line, ridden on the x-pramaan-correlation-id header, and carried in-message on the agents rail — and the OpenTelemetry trace_id, which is what X-Ray and CloudWatch actually key on. Two id systems that half-overlap is worse than either alone: a human chasing a request has to guess which key the surface in front of them speaks, and the cid was regenerated at the WebSocket edge and ignored by the brain, so it was never continuous end-to-end anyway. The founder decision (2026-06-19, ratified in pramaan-docs ADR-124) is to retire the duplicate while it is cheap — zero customers, no SLA, no compatibility debt — and standardize on W3C trace_id as the single technical request identity. Stories #1453/#1454/#1455 already repointed the error-response field and the app/admin clients off cid; this Story (roster id R7d) is the functions-lane rip-out: the cid machinery itself is deleted so trace_id is the sole correlator.
What is deliberately preserved. trace_id / traceparent / tracestate, the lambda_wrapper W3C extraction, the SQS message-attribute trace carrier, the active-span trace read, and the Atlas business key run_id are untouched. run_id rides a separate axis (a business key, never used to rebuild trace continuity) — its occurrence count in src/ is unchanged by this Story. Only the cid is removed.
The Fix: Pull One Whole ID System Out
The cid lived in five shapes — a module, a middleware, a contextvar, log keys, and DTO/health fields. Each is removed at its root, and every importer is cleaned up so nothing references the deleted symbols.
BEFORE AFTER
correlation.py (module + ContextVar) -> deleted
CorrelationIdMiddleware -> ErrorTraceIdMiddleware (trace_id-only)
log line { ..., correlation_id, ... } -> log line { ... } (key gone)
HealthResponse.correlation_id -> removed
AgentCommand.correlation_id -> removed (run_id/traceparent stay)
InvokeMeta.correlation_id -> removed (agent_invoke.v1 meta)
WS error body { correlation_id, ... } -> { trace_id, ... } (repointed)
x-pramaan-correlation-id header read -> removed
src/shared/observability/correlation.py(deleted) — the cid module: the UUIDv4 validator, thecorrelation_id_varContextVar, and theget/set/reset/resolvehelpers. Gone.src/shared/observability/middleware.py—CorrelationIdMiddlewarebecomesErrorTraceIdMiddleware. The cid resolution, the contextvar set/reset, the route-spancorrelation_idattribute, and thex-pramaan-correlation-idresponse header are all removed. The Story-1453 trace-id error-body backfill (_with_error_trace_id+ the catch-all 500 envelope) is kept verbatim — the framework-error surface still publishes the active span’strace_id.src/shared/observability/logger.py— the log shape v1 line drops itscorrelation_idkey (and the_log_correlation_idminting helper).trace_id/span_idenrichment from the OTel log instrumentor is unchanged.src/shared/dtos/health.py+ the two health routes —HealthResponse.correlation_idis removed, and theresponse_model_exclude={"correlation_id"}guard it required disappears with it.src/shared/agent_commands.py,src/shared/agent_contracts/invoke.py— the optionalcorrelation_idfield is removed from the queueAgentCommandand from theagent_invoke.v1InvokeMeta. The brain already ignored cid, so dropping the producer field is safe;traceparent/run_idcarry identity forward.src/app_api/router_ws.py— the WS Lambda stops resolving/setting cid and stops stamping it on the envelope. The WS error body field is repointed fromcorrelation_idtotrace_id(sourced fromget_current_trace_id()), matching the #1453 error surface.src/clerk_worker/{handler,runner}.py,src/rail_canary/handler.py, request/payload loggers, metric helpers — every remaining cid read, contextvar token, log field, and metric-metadata key is removed. The SQStraceparentextraction andmanual_spantrace context in the worker are left intact.openapi/app-api.json,openapi/admin-api.json— regenerated viamake openapi; thecorrelation_idproperty is gone from both Health schemas, no other drift.
Why the Agents Rail Had To Be Swept Too
It would have been smaller to delete only correlation.py and the middleware. That fails to compile: router_ws, the clerk worker, and the rail canary all imported the cid helpers. Deleting the module forces removing every importer — which is exactly the point. The acceptance bar is grep-zero cid in src/, not “the obvious files only.” A half-removed id system leaves dangling imports and a log key that some surfaces emit and others don’t — the precise two-systems-half-coexist mess EPIC-1400 exists to end.
Acceptance Evidence
| Acceptance criterion | Check |
|---|---|
grep-zero cid in src/ | grep -rniE 'correlation' src/ → no matches (exit 1) |
grep-zero cid in openapi/ | grep -rniE 'correlation' openapi/ → no matches (exit 1) |
run_id occurrence count unchanged | grep -rni 'run_id' src/ = 215 on this branch and on main |
| Story-1453 trace-id error body stays green | test_error_response_envelope.py (PramaanError body trace_id == TRACE_ID_HEX) + test_error_trace_id_middleware.py (422/500 ASGI + Lambda backfill) |
| Connected-graph plumbing intact | SQS traceparent extraction (test_handler), enqueue injects trace context but no cid (test_enqueue_injects_trace_context_message_attributes), lambda_wrapper W3C extraction unchanged |
| Full suite green | 954 passed against the isolated Docker test DB; ruff clean; make openapi idempotent; git diff --check clean |
| Walkthrough doc included | this document |
Review gate — did deleting cid sever any live trace edge or touch run_id? No. Trace continuity rides traceparent/trace_id, which is untouched: the SQS carrier, the worker’s manual_span context extraction, and the active-span read all remain. run_id is a separate business-key axis and its count is byte-for-byte unchanged (215 = 215). The brain already ignored cid, so removing the producer field on AgentCommand/InvokeMeta orphans nothing. The only behavioural change to a response surface is the WS error body field, which is repointed to the real trace_id — the same identity the #1453 HTTP error surface already publishes.
Recall: Story 1456 is the functions-lane funeral for correlation_id — the module, middleware, contextvar, log key, health field, agents-rail in-message field, and the x-pramaan-correlation-id header all deleted — leaving W3C trace_id as the single request correlator and run_id preserved as the Atlas business key.