You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #76 (#41 first cut) introduced TurnRecord.fidelity (granularity + per-field coverage + derived class) and the helpers EMPTY_COVERAGE, classifyFidelity, makeFidelity in @relayburn/reader. The Claude parser populates fidelity on every turn (packages/reader/src/claude.ts:255,1049 via buildClaudeFidelity at :604).
The Codex parser does not. Quoting PR #76's deferred-work paragraph:
Codex and OpenCode parsers (they still emit no fidelity; consumers treat absence as best-effort full for backward compat).
packages/reader/src/codex.ts constructs TurnRecord objects at :513-543 without any fidelity field. As a result, summarizeFidelity (packages/analyze/src/fidelity.ts) buckets every Codex turn under unknown, and hasMinimumFidelity(undefined, …) passes silently — the exact "0 vs unknown" ambiguity #41 was filed to fix.
Proposal
Populate TurnRecord.fidelity in parseCodexSessionIncremental (packages/reader/src/codex.ts) on every emitted turn, mirroring the Claude approach in buildClaudeFidelity.
Coverage for Codex rollouts (event_msg.token_count payload at :283-294):
hasInputTokens: true — total_token_usage.input_tokens is the source of truth.
hasRawContent: true — full content capture path exists when contentMode === 'full'.
Granularity: 'per-turn'. The cumulative-delta arithmetic in finalizeTurn (:597) gives true per-turn token counts.
If a token-count event is absent for a turn (no task_complete-bound total_token_usage observed before the turn closes), set hasInputTokens / hasOutputTokens / hasReasoningTokens / hasCacheReadTokens to false for that turn so the resulting class is partial rather than a silent zero.
Also follow Claude's pattern of treating hasToolCalls / hasToolResultEvents / hasRawContent as capability flags (true even when a particular turn has no tools), not presence.
Acceptance criteria
Every TurnRecord emitted by parseCodexSession / parseCodexSessionIncremental carries a fidelity field.
granularity === 'per-turn'.
Coverage flags follow the matrix above; hasReasoningTokens is true when a Codex token_count was observed for the turn.
A turn whose source omitted total_token_usage reports class === 'partial', not 'full', and the missing usage flags are false — usage numeric fields can still be 0 (the existing post-hoc default) but the coverage flag is the honest signal.
New tests in packages/reader/src/codex.test.ts cover at minimum: a full-fidelity turn (all usage fields present), a turn missing token_count (partial), and a turn with both function_call and function_call_output (tool-result coverage stays true).
summarizeFidelity over a real Codex session yields unknown === 0 for the produced turns.
Context
PR #76 (#41 first cut) introduced
TurnRecord.fidelity(granularity + per-field coverage + derived class) and the helpersEMPTY_COVERAGE,classifyFidelity,makeFidelityin@relayburn/reader. The Claude parser populates fidelity on every turn (packages/reader/src/claude.ts:255,1049viabuildClaudeFidelityat:604).The Codex parser does not. Quoting PR #76's deferred-work paragraph:
packages/reader/src/codex.tsconstructsTurnRecordobjects at:513-543without anyfidelityfield. As a result,summarizeFidelity(packages/analyze/src/fidelity.ts) buckets every Codex turn underunknown, andhasMinimumFidelity(undefined, …)passes silently — the exact "0 vs unknown" ambiguity #41 was filed to fix.Proposal
Populate
TurnRecord.fidelityinparseCodexSessionIncremental(packages/reader/src/codex.ts) on every emitted turn, mirroring the Claude approach inbuildClaudeFidelity.Coverage for Codex rollouts (
event_msg.token_countpayload at:283-294):hasInputTokens: true—total_token_usage.input_tokensis the source of truth.hasOutputTokens: true—total_token_usage.output_tokens.hasReasoningTokens: true—reasoning_output_tokensis surfaced; this is one of the few sources where it is.hasCacheReadTokens: true—cached_input_tokens.hasCacheCreateTokens: false— Codex rollouts have no ephemeral cache-create concept.hasToolCalls: true—function_call/custom_tool_callitems are captured.hasToolResultEvents: true—function_call_output/custom_tool_call_outputitems are captured (:430-449).hasSessionRelationships: falsefor now — Codex rollouts have no parent-tracking path (per Parity approach: Codex and OpenCode spawn-tagging + incremental ingest without native hooks #63); flip when the spawn-tagging substrate lands.hasRawContent: true— full content capture path exists whencontentMode === 'full'.Granularity:
'per-turn'. The cumulative-delta arithmetic infinalizeTurn(:597) gives true per-turn token counts.If a token-count event is absent for a turn (no
task_complete-boundtotal_token_usageobserved before the turn closes), sethasInputTokens/hasOutputTokens/hasReasoningTokens/hasCacheReadTokenstofalsefor that turn so the resultingclassispartialrather than a silent zero.Also follow Claude's pattern of treating
hasToolCalls/hasToolResultEvents/hasRawContentas capability flags (true even when a particular turn has no tools), not presence.Acceptance criteria
TurnRecordemitted byparseCodexSession/parseCodexSessionIncrementalcarries afidelityfield.granularity === 'per-turn'.hasReasoningTokensistruewhen a Codextoken_countwas observed for the turn.total_token_usagereportsclass === 'partial', not'full', and the missing usage flags arefalse—usagenumeric fields can still be0(the existing post-hoc default) but the coverage flag is the honest signal.packages/reader/src/codex.test.tscover at minimum: a full-fidelity turn (all usage fields present), a turn missingtoken_count(partial), and a turn with bothfunction_callandfunction_call_output(tool-result coverage staystrue).summarizeFidelityover a real Codex session yieldsunknown === 0for the produced turns.Out of scope
hasSessionRelationshipsto a real signal — that needs the parity work in Parity approach: Codex and OpenCode spawn-tagging + incremental ingest without native hooks #63 and the substrate in Execution graph for passive readers: session relationships and tool-result event chronology #42.Refs