Story 1435: HTTP to SQS to clerk worker stays one trace
This Story cuts the clerk-run SQS hop over to W3C trace context. The WebSocket broker injects traceparent/tracestate into SQS message attributes, and the clerk worker extracts those attributes before opening the approved functions.agent_queue.process_record consumer span.
Why
The failure mode is an orphaned worker trace: the browser hits the WebSocket handler, the handler enqueues the Atlas run, then the worker starts a separate trace with no parent edge. In X-Ray, that looks like two unrelated events even though one user action caused both. Story 1435 fixes the boundary where that split happens.
What changed
| Surface | Change |
|---|---|
src/app_api/router_ws.py |
_trace_message_attributes() now sends only W3C trace context attributes. The SQS correlation_id attribute is removed. |
src/clerk_worker/handler.py |
The worker reads messageAttributes.traceparent.stringValue and optional tracestate.stringValue, extracts them with TraceContextTextMapPropagator, and passes that context to the shared manual_span helper. |
tests/unit/test_router_ws.py |
Producer tests assert traceparent is emitted and correlation_id is not. |
tests/unit/clerk_worker/test_handler.py |
Consumer test proves the SQS traceparent becomes the remote parent context for the manual consumer span. |
Instrumentation model
T1 SQS send/receive plumbing remains library-owned by ADOT. This change adds the T3 registry-approved domain span only at the record-processing boundary:
functions.agent_queue.process_record
SpanKind: CONSUMER
Parent: extracted from SQS messageAttributes.traceparent.stringValue
Batch shape: documented batch_size=1 parent-child exception
Semconv check
| Standard keys emitted | Custom PRAMAAN keys emitted |
|---|---|
messaging.system, messaging.operation.name, messaging.operation.type, messaging.destination.name |
firm_id, run_id, agent.key |
http.method, http.status_code, correlation_id, cid, or baggage key is introduced. firm_id is a span attribute only, never baggage.Verification
uv run pytest tests/unit/test_router_ws.py tests/unit/clerk_worker/test_handler.py \
tests/unit/shared/observability/test_manual_span.py \
tests/unit/test_observability_guardrail_check.py -q
uv run ruff check src/app_api/router_ws.py src/clerk_worker/handler.py \
tests/unit/test_router_ws.py tests/unit/clerk_worker/test_handler.py
.github/scripts/observability-guardrail-check.sh
git diff --check
Staging proof still requires a deployed request in X-Ray: a WebSocket launch should show the WebSocket handler, SQS send, and clerk-worker functions.agent_queue.process_record span as one parent-child trace.