Repository navigation
[G1] Capture per-message raw facts burn needs: requestId, stop_reason, agent version, sidechain/meta flags, Codex turn ids #164
Description
Part of #160 (group 1 — independent).
Problem
session_events (crates/ai-hist-core/src/lib.rs:138-155) stores message_id, parent_id, ts_ms, role, kind, text, model, token_json, event_uid. burn's analytics need per-message facts that relayhistory currently reads past. grep -rn "requestId\|request_id\|stop_reason" crates/ai-hist/src/lib.rs crates/ai-hist-core/src/lib.rs returns nothing.
What burn reads today and where it uses it (all in AgentWorkforce/burn, crates/relayburn-sdk/src/):
| Raw field | Harness | burn reader | burn consumer |
|---|---|---|---|
requestId / request_id (envelope) |
Claude | reader/claude.rs:1310-1312, claude/incremental.rs:821-830 |
reader/inference.rs:236 groups turns into API requests by requestId (RequestIdLookup); falls back to message_id for harnesses without one |
message.stop_reason |
Claude | claude.rs:370-470 |
reader/claude/span_tree.rs; in-progress-turn detection (claude/incremental.rs:646-664 backs the cursor up to the earliest assistant message without a stop_reason) |
step-finish.reason |
OpenCode | reader/opencode.rs:544-553 → StopReason::from_wire |
same |
version / sourceVersion |
Claude | claude/relationships.rs (source_version) |
relationship records, version-dependent quirks |
isSidechain, isMeta |
Claude | claude.rs |
sidechain attribution |
turn_id from turn_context; task_started/task_complete boundaries |
Codex | codex/incremental.rs:230-336 |
Codex turn boundaries (burn #504 is about exactly this) |
cache_creation.ephemeral_5m_input_tokens / ephemeral_1h_input_tokens |
Claude | claude.rs:587-643 |
Usage.cache_create_5m/1h for pricing |
relayhistory stores message.usage verbatim in token_json (lib.rs:3070-3072), so the nested cache_creation object is already preserved — add a test proving it survives round-trip.
Scope
- Schema (
crates/ai-hist-core/src/lib.rsSCHEMA+init_db_lockedmigration): add nullable columns tosession_events:request_id TEXT,stop_reason TEXT,agent_version TEXT,is_sidechain INTEGER,is_meta INTEGER,turn_id TEXT. Add a migration marker (follow thesession_relationships_v2pattern) and bumpHYDRATION_PARSER_VERSION(crates/ai-hist/src/hydrate.rs:15) so existing databases re-hydrate and backfill. - Claude parser (
crates/ai-hist/src/lib.rs:2999-3302): readrequestId/request_id,message.stop_reason,version/sourceVersion,isSidechain,isMeta. Storestop_reasonas the verbatim wire string (no enum mapping here — burn maps; relayhistory preserves). - Codex parser (
lib.rs:2101-2508): readturn_context.payload.turn_idand stamp it on every event until the nextturn_context. (task_started/task_completemarkers belong to the "dropped record types" group-1 issue; this one ownsturn_idon events.) - OpenCode:
step-finish.reason→stop_reasonon the assistant message events (only meaningful once the OpenCode parity issue lands; land the column and the mapping hook now, with a test that is#[ignore]until then). - Expose the new columns on
SessionEvent(crates/ai-hist-core/src/lib.rs:1512),NativeSessionEvent(crates/ai-hist-napi/src/lib.rs:249),sdk-ts/src/contracts.ts:185andnormalization.ts:177; bumpSESSION_EVIDENCE_CONTRACT_VERSIONandNATIVE_CONTRACT_VERSIONperdocs/architecture.md. docs/session-catalog.mdcapability matrix rows for the new fields per source.
Acceptance
- Fixture-corpus snapshots show
request_idandstop_reasonpopulated for every Claude assistant message inmulti-block-turn, andstop_reasonabsent for the in-progress message inincomplete-then-complete. - Round-trip test:
token_jsonfor a Claude message withcache_creation: {ephemeral_5m_input_tokens, ephemeral_1h_input_tokens}returns both nested values. - Codex
multi-turnsnapshot showsturn_idchanging at eachturn_context. - Existing databases upgrade in place;
ai-hist sessions hydratereportsstatus: "updated"once after upgrade andunchangedafter.
Out of scope
Mapping stop reasons to a normalized enum (burn does this in types.rs:287-357); grouping events into inferences.
Activity