Invariants / DC-NODE-40

DC-NODE-40

DC derived enforced

Rolled-back branch evidence retention for the LCA walk (PHASE4-N-AO S13). Rolled-back blocks MAY be retained only as walk-visible EVIDENCE for future competing-branch reconstruction: k-bounded (block depth), hash-keyed (BTreeMap, never HashMap-iterated for semantic ordering), self-binding (map key == re-derived block hash; a mismatch fails closed, CacheSelfBindingViolation). They NEVER become durable authority, a rollback target, or the LCA anchor, and NEVER bypass S2 header validation or S4 body prevalidation. The durable LCA remains the ChainDb stored slot+hash ONLY (DC-NODE-29); the retention merely lets walk_to_durable_lca traverse non-durable INTERMEDIATE headers -- exactly the blocks Ade itself rolled back during a ForkChoiceWin adoption -- until it reaches a real durable ancestor. This closes the Fault-1 MissingBridge OVER-FIRE: after cn1->cn2 switch, cn1's own rolled-back blocks (admitted LinearExtend, never in the S7 competing-only branch cache) are retained so cn1's later competing blocks are EVALUABLE (fork-choice resolves them -- they lose) instead of falsely un-bridgeable (BranchGap -> MissingBridge -> fence-held on every loser block, a producer-liveness risk). MissingBridge (DC-NODE-39) is then reserved for a GENUINE gap (neither durable nor cache nor retention has the bridge -- Fault 2). REPLAY-EQUIVALENT: same rollback + same retained set -> same walk verdict.

Source

docs/planning/phase4-n-ao-ce-ao-6-live-gap.md (S11 Fault-1 finding) + docs/clusters/PHASE4-N-AO/S13-rolled-back-branch-evidence-retention.md

Introduced in
PHASE4-N-AO

Enforcement trace

Tests 6

  • rollback_retains_removed_blocks_for_lca_walk
  • retained_blocks_are_not_anchors
  • retained_blocks_are_k_bounded
  • retained_block_hash_self_binds
  • genuine_gap_still_missing_bridge
  • apply_fork_switch_populates_rollback_retention

Cross-references

Attack rationale

Without retaining the rolled-back blocks, the LCA walk cannot reconstruct a competing branch that descends through Ade's OWN recently-rolled-back chain, so every such block fails BranchGap -> MissingBridge -> fence-held. For a follower this is harmless (clears on each winner admit); for a PRODUCER it is a liveness denial -- normal competitor traffic after a fork-switch would intermittently hold the forge fence. Retaining the rolled-back blocks as EVIDENCE (not authority) restores evaluability without widening trust: the retention is self-binding (a peer cannot inject a lying header under a hash it doesn't claim), k-bounded (no unbounded memory / no over-k branch), and never the durable anchor (the LCA is still ChainDb-only, so a retained block cannot redirect selection or become a rollback target), and never bypasses S2/S4 (a retained header still validates before any adoption). The narrow retention surface -- exactly the blocks Ade rolled back -- keeps the change auditable vs caching every received header (broader trust/eviction surface) or full per-peer candidate tracking (a larger architectural change that would destabilize S1-S12).

Evidence notes

Declared at PHASE4-N-AO S13. Motivated by the 2026-06-13 S11 floor-fires-live grind (preserved ~/.cardano-ceai6/ao-s11-floor-fires-live-{conv.jsonl,FFDIAG.txt}): a converging run emitted 23 missing_bridge events ALL from the LOSER cn1, all branch_gap, with cn1 block_no fully CONSECUTIVE 9->37 (Ade received the entire branch). Root: cn1 bno10-14 LinearExtend-admitted pre-switch, rolled back on cn2 adoption, never in the competing-only S7 cache -> the walk for cn1 bno15+ hits the rolled-back bno14 -> BranchGap. Enforced at S13 close via 5 hermetic tests (rollback_retains_removed_blocks_for_lca_walk, retained_blocks_are_not_anchors, retained_blocks_are_k_bounded, retained_block_hash_self_binds, genuine_gap_still_missing_bridge) + ci_check_rollback_retention_evidence.sh + the S12 harness still emitting MissingBridge on a genuine gap + a live run with the over-fire gone.