feat(export): name each trace request's source row in trajectory metadata
Stacked on #40 (steffen/dynamo-trace-export); merge that first.
To compare a replayed request with the latency production recorded for it, the replay results have to join back to the trace rows. Each trajectory exported from Dynamo traces now carries metadata.requests, a list aligned with the trajectory's requests:
"metadata": {"source": "dynamo-request-trace", "model": "...", "block_size": 64,
"requests": [
{"request_id": "req-0007", "x_request_id": "00000000-0000-7000-8000-0000000000d1", "received_ms": 1788264001000},
{"request_id": "req-0008", "x_request_id": null, "received_ms": 1788264005000}]}
request_idis the kept row'srequest.request_id. For a retried request, that is the attempt the exporter keeps.x_request_idis in the lower-cased form the analytics join uses, or null when the row has none.received_msis the row'srequest_received_ms.
The manifest schema is otherwise unchanged. The agent parses trajectory metadata as an arbitrary JSON value (TrajectoryPlanSpec::_metadata: Option<Value>), and the exporter's validator does not inspect it, so neither needs a change. A replay with --request-events-jsonl (#41) keys its records by trajectory_id and invocation, so metadata.requests[invocation - 1] names the source row.
docs/export-plans.md documents the list and adds request.request_id to the fields read from each row.
Testing
python -m unittest discover -s tests: 38 passed, none skipped. The new test covers a retried request (the dropped attempt's id does not appear), a lower-cased x_request_id, and a row without one. The existing metadata assertion now includes the list. Both exported-manifest dry runs through batchbench-agent ran and pass, so the agent accepts the new metadata.
🤖 Generated with Claude Code