Skip to content

[G1] Characterization lock: ledger golden snapshots for the full fixture corpus and a burn state parity ledger differ #555

Description

@willwashburn

Part of #553 (group 1 — independent; no relayhistory work required). The reader swap in group 2 is only safe if today's ledger output is pinned first.

Problem

burn's correctness for a reader swap is defined by the rows the ledger ends up with, not by the parser's in-memory structs. The existing guards are partial:

  • crates/relayburn-cli/tests/golden.rs snapshots CLI stdout for a handful of invocations over tests/fixtures/cli-golden/ledger.jsonl — it locks presentation, not ledger rows.
  • crates/relayburn-sdk/src/reader/{claude,codex,opencode}/tests.rs assert struct fields per fixture — they will be deleted with the readers at cutover, taking their expectations with them.
  • ledger/fingerprint.rs is the silent-collapse hazard: turn_content_fingerprint (:97-115) = sha256("{ts}|{model}|{input+output}|{cacheRead}|{cache5m+cache1h}|{first tool args_hash[..4]}")[..16]; if a new source derives ts, message_id, or args_hash differently, turns are either duplicated (different turn_id_fingerprint, same content) or collapsed. turn_index is pass-local (claude/incremental.rs:719, codex/incremental.rs:929, opencode.rs:220) and must not be part of any identity.

Scope

  1. Ledger snapshot harness crates/relayburn-sdk/tests/ledger_snapshots.rs: for every fixture in tests/fixtures/{claude,codex,opencode} (27 + 8 + 5) and every multi-file case (original-session+fork-branch-a+fork-branch-b; cross-file-parent; sidechain-*; nested-subagent with its sidecars), run ingest_all into a temp ledger with IngestRoots pinned to a temp HOME, then dump every DERIVABLE_TABLES row (ledger/schema.rs:18-30: turns, compactions, relationships, tool_result_events, user_turns, sessions, inferences) plus content (content.sqlite) as canonical JSON (sorted keys, rows ordered by primary key, record_json parsed and re-serialized stably) to tests/snapshots/ledger/<case>.json. Also snapshot the archive_state.upstream_cursors_json shape and source_fingerprint per case. UPDATE_SNAPSHOTS=1 regenerates.
  2. Incremental equivalence: for each JSONL fixture, ingest in 1 pass vs. in N passes (append line-by-line or in 3 chunks, re-running ingest_all between) and assert identical snapshots. This pins the in-progress/back-off cursor semantics (claude/incremental.rs:646-664, codex/incremental.rs:790-843) that a relayhistory-backed source must reproduce.
  3. Identity vectors: a table-driven test listing, for each fixture turn, (source, session_id, message_id, ts, turn_id_fingerprint, turn_content_fingerprint) — the values the group-2 adapter must reproduce exactly. Same for tool_result_event (tool_use_id, event_index) and user_turns.user_uuid (note the synthesized Codex/OpenCode form "{session}:{preceding||start}->{following}", codex.rs:746-769, opencode.rs:965-973).
  4. burn state parity --against <other-ledger-home> [--session id] [--json]: a maintenance verb in crates/relayburn-cli/src/commands/state.rs (keep it under state per AGENTS.md) that diffs two ledgers table-by-table by primary key and reports: rows only in A, only in B, same key but different record_json (with a field-level diff), and summary totals (turns, tokens by model, cost). Backed by an SDK function in query_verbs/state.rs so tests and the group-2 parity harness can call it. This is the tool that will prove the swap on a real machine ("confirm an operation against its effect, never its return value").
  5. Add the git canonicalization vectors (git@github.com:Org/Repo.git, https://github.com/org/repo, ssh://git@host:2222/org/repo.git, no-remote, worktree) to reader/git.rs tests so relayhistory's project_key helper can be checked against the same expectations.

Acceptance

  • cargo test -p relayburn-sdk --test ledger_snapshots passes; snapshots committed; CI runs it.
  • Incremental-equivalence test covers every JSONL fixture.
  • burn state parity --against on two ledgers built from the same fixtures reports zero differences; on a ledger where one turn's ts is shifted by 1 ms it reports exactly one turns row differing and one turn_content_fingerprint change.
  • AGENTS.md "Ledger schema" bullet mentions the snapshot harness as the thing to update when the on-disk shape changes.

Out of scope

Any behaviour change; comparing against relayhistory (group 2).

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions