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

SurfaceChange
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 emittedCustom PRAMAAN keys emitted
messaging.system, messaging.operation.name, messaging.operation.type, messaging.destination.name firm_id, run_id, agent.key
No 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.