Invariants / DC-NODE-39

DC-NODE-39

DC derived enforced

Post-ForkChoiceWin forward-follow continuity (PHASE4-N-AO S11). After a ForkChoiceWin adoption at tip X, Ade must continue receiving and admitting the winning peer's descendants in PARENT-LINK ORDER (X -> X+1 -> X+2, each via validated prev_hash == prior tip), OR fail closed with a STRUCTURED missing-bridge reason; it must NOT silently skip a required bridge block and stall behind the winning branch. A descendant whose parent link Ade has not validated is never admitted (no peer-claimed bridging -- the parent must be in Ade's validated store / proven branch). On a genuinely missing bridge (peer serves X+2 without X+1) the post-switch admit path emits a closed MissingBridge discriminant, PRESERVES the current durable chain byte-unchanged (no rollback, no admit, no adoption -- MissingBridge is never a rollback target / candidate anchor / fence-clear reason), HOLDS the forge fence (pending_missing_bridge.is_none() is now a fence-resolve precondition), and refuses the silent no-op -- never a silent stall and never a forced/guessed admit. The hold is HOLD-until-progress: a real LinearExtend admit (pump_block Some) or a proven fork-switch adoption clears it, so a late-arriving bridge releases the fence. REPLAY-EQUIVALENT: given the same post-switch served sequence and the same adopted X, Ade derives the same admit / MissingBridge outcome byte-identically. This is the robustness half of CN-CONS-03: convergence is not 'Ade can complete once' (S10/run 2 proved capability) but 'Ade reliably continues on the selected branch or fails closed structurally', closing the conditional follow-forward hole run 1 surfaced (adopted cn2@298, received 388 but missed the bridge ~340, stalled fail-closed-but-no-progress).

Source

docs/planning/phase4-n-ao-ce-ao-6-live-gap.md (run-1 root-cause finding) + docs/clusters/PHASE4-N-AO/S11-post-forkchoicewin-forward-follow.md

Introduced in
PHASE4-N-AO

Enforcement trace

Tests 7

  • post_switch_admits_winner_descendant_x_plus_1
  • post_switch_missing_bridge_emits_structured_and_holds_fence
  • missing_bridge_wrong_parent_maps_closed_code
  • late_bridge_clears_hold_on_progress
  • missing_bridge_reason_maps_lca_error_to_closed_discriminant
  • bridge_gap_injection_emits_missing_bridge
  • late_bridge_recovers_on_progress

Cross-references

Attack rationale

A silent post-switch stall is fail-closed (no divergence, no mis-admit) but is NOT convergence: a node that adopts the fork-choice winner then cannot follow it forward is stuck behind the canonical chain indefinitely -- a liveness failure that, under adversarial timing, lets a peer strand Ade by serving descendants out-of-order or omitting a bridge. Marking CN-CONS-03 enforced on a single capability pass (run 2) while the same binary conditionally stalls (run 1) would overstate the invariant. DC-NODE-39 forces the post-switch path to either complete in parent-link order or fail closed with a STRUCTURED reason (so the stall is observable + halts deterministically, never silent), without weakening adoption authority or admitting an unvalidated bridge. The peer's served sequence is observed evidence; the admit decision stays on Ade's validated parent links.

Evidence notes

Declared at PHASE4-N-AO S11. Motivated by the 2026-06-13 run-1 finding (gap doc): adopted cn2@298, the winner's descendants 388/422/459 were RECEIVED but each classified Competing with 0 post-switch lca_discovered -> 388.prev is a cn2 block Ade lacks (a HOLE), so neither LinearExtend nor LCA-bridge -> silent stall at X. Run 2 (preserved ~/.cardano-ceai6/ao-s10-run2-PASS-conv.jsonl) proved the mechanism CAN complete (adopted cn1@247, 19 chained descendants, agreed@X, 0 diverged -- passes ci_check_post_switch_convergence_window.sh). S11 entry gate = an instrumented diagnostic answering peer-fault (bridge never served) vs Ade-fault (served-then-dropped/misclassified/out-of-order) BEFORE the fix is scoped. Enforced at S11 close via the fail-closed MissingBridge gate + hermetic happy-path/missing-bridge tests + a live re-run that no longer reproduces the stall. CN-CONS-03 flips ONLY then.