fix(trace): prevent Codex token overcounting and separate cache usage
Codex rollouts can contain two cumulative usage streams that diverge after compaction. Trace mixed them against one baseline and treated lower snapshots as resets, inflating the supplied session from 51,971,562 inclusive tokens to 5,260,816,481.
Prefer per-response native usage, deduplicate response IDs, and select one cumulative source for older transcripts. Decreasing cumulative snapshots keep their previous baseline and produce a warning. Normalize Codex evidence to match Claude: input and total exclude cache, both cache fields remain in evidence, and cost includes all categories at their respective rates.
Codex now emits data.schema_version: v2 to identify these cache-exclusive counts. Legacy Codex v1 counts include cache. The accounting document describes how consumers distinguish and normalize the two versions; no new evidence fields are needed.
The sanitized regression fixture retains all 275 responses and 275 mirrored events. It now produces 20,425 base input tokens, 51,113,536 cache-read tokens, 669,175 cache-write tokens, and 168,426 output tokens, with no warnings and an estimated cost of $8.5094. Adding the disjoint fields reconstructs the original response totals.
Add a Cache token accounting row to the feature README and wire it into the end-to-end suite using the existing edit-and-commit scenario. The check compares stored counts with raw Codex/Claude usage, includes Claude subagents, and reports sessions without cache activity as not covered. Checker regressions reject missing cache fields, cache included in base input/total, double-counted reasoning, and self-consistent inflated aggregates. Parser regressions also cover duplicate response replay, resumed sessions, model switches, cumulative fallbacks, and cache pricing.
Cumulative fallback snapshots can advance even when their response IDs repeat. The end-to-end checker compares against the accepted cumulative high-water snapshot, including when the last raw snapshot decreases. These cases have dedicated regressions. Response-ID deduplication applies only to per-response usage. Claude's check preserves raw usage counts but does not detect duplicate assistant records. Both new live-matrix cells remain Unknown until the matrix runs.
Validation:
go test ./app/cli/internal/trace/... ./pkg/attestation/crafter/materials/aicodingsession/... ./internal/schemavalidators ./app/cli/cmdgolangci-lint run ./app/cli/internal/trace/codex/... ./app/cli/internal/trace/e2e/...git diff --check
The live agent matrix was not rerun. Existing uploaded evidence is unchanged; the accounting document explains re-parsing and display implications. Other providers' accounting is audited in the document, but their parser behavior is unchanged. Claude deduplication, Pi's selected-branch scope, and OpenCode reasoning accounting remain follow-up work.
Related to #3706.