Invariants / DC-WAL-02

DC-WAL-02

DC derived enforced

WAL fingerprint-chain integrity: every WalEntry::AdmitBlock has prior_fp == previous entry's post_fp (or anchor's initial_ledger_fingerprint for the first entry). WalStore::verify_chain walks the WAL + asserts the chain holds. Verify failure is authority-fatal at the binary boundary.

Source

docs/planning/phase4-n-m-ledger-seed-invariants.md §1 (I-A5)

Enforcement trace

Tests 6

  • crates/ade_runtime/src/wal/file_wal_store.rs::tests::file_wal_store_verify_chain_passes_then_catches_break
  • crates/ade_ledger/src/wal/replay.rs::tests::replay_from_anchor_catches_chain_break
  • crates/ade_runtime/tests/wal_replay_from_anchor.rs::wal_replay_from_anchor_rejects_chain_break
  • crates/ade_node/src/node_sync.rs::tests::recover_follow_kill_warm_start_chains_from_ledger_fp
  • crates/ade_node/src/node_sync.rs::tests::recover_follow_zero_seed_chainbreaks
  • crates/ade_node/src/node_sync.rs::tests::recover_follow_two_runs_byte_identical

Cross-references

Strengthened in

Evidence notes

PHASE4-N-M-A S3+S4 (2026-05-26): WalStore::verify_chain walks every entry asserting prior_fp == previous post_fp (anchor's initial_ledger_fingerprint for the first entry). ChainBreak is authority-fatal; tests prove the verifier catches injected corruption. PHASE4-N-AE.C (2026-06-07): the FIRST-ENTRY clause is now enforced on the LIVE recover->follow path. The CE-A5 live run surfaced node_lifecycle seeding the follow ForwardSyncState prior_fp with Hash32([0u8;32]) (zero) instead of fingerprint(&state.ledger).combined -- so the first followed AdmitBlock's prior_fp was 0, not the recovered ledger-tip post_fp, and a recover->followed store failed warm-start (ChainBreak@1, exit 42). Fixed at both lifecycle sites (forge-off + forge-on); recover_follow_zero_seed_chainbreaks reproduces the break, recover_follow_kill_warm_start_chains_from_ledger_fp proves the fix, and ci_check_recover_follow_wal_lineage.sh fences the live seed (without loosening verify_chain).