DC-NODE-08
DC derived enforced--mode node MAY forge the genesis-successor (FIRST) block from the recovered authoritative base when ChainDb::tip() AND the recovered tip (recovered.tip) are BOTH None, but ONLY when ALL hold: (a) the explicitly WarmStart-recovered/imported seed-epoch lineage is present (the recovered SeedEpochConsensusInputs + anchor lineage), never an unanchored / from-genesis-file-constructed or stale base; (b) ForgeIntent::On with complete operator key material; (c) the feed state is forge-eligible under the CN-NODE-04 closed split (no_block_available | clean_empty), never an ineligible / ambiguous (unknown_disconnected) or error state; (d) the slot/epoch/KES/leader guards pass (DC-EPOCH-03 single-epoch containment + the BLUE leader check + KES-period/opcert); and (e) the forged first block carries PrevHash::Genesis (CBOR null per CN-WIRE-09) and flows through self_accept -> SelfAcceptedHandoff (DC-NODE-06) -> ServedChainView (DC-NODE-07). The recovered lineage gates PERMISSION to forge from the genesis-successor position; it is NOT the source of the prev_hash bytes, which are structurally null. The first-block reachability fires EXACTLY ONCE; once a durable tip exists, block_number > 0 takes the normal selected_tip path with PrevHash::Block. The durable tip advances ONLY through the accepted path, never from forge scheduling alone; no forge from raw / unanchored genesis; the forge base is the recovered surface only (CN-CINPUT-03 / DC-CINPUT-02b); no RO-LIVE-01/06 flip. The eligibility signal is general (forge-configured + valid recovered base), never a private-only / C1-only flag.
- Source
docs/planning/phase4-n-f-g-j-genesis-successor-prevhash-invariants.md
- Cluster
- PHASE4-N-F-G-J
- Introduced in
- PHASE4-N-F-G-J
Enforcement trace
Code
Tests 9
- forge_one_from_recovered_cold_start_is_block_zero_genesis
- forge_one_from_recovered_with_tip_is_block_n_plus_one_block_prev
- forge_header_position_some_tip_without_block_no_fails_closed
- cold_start_block_number_is_zero_single_convention
- node_spine_cold_start_forges_genesis_block_zero
- cold_start_gate_allows_genesis_when_eligible_and_recovered
- node_spine_cold_start_ineligible_feed_does_not_forge
- cold_start_gate_blocks_without_recovered_lineage
- cold_start_gate_inactive_when_tip_present
Cross-references
Evidence notes
PHASE4-N-F-G-J S4 (2026-06-03) flipped declared -> enforced. Node-spine cold-start first-block reachability: when ChainDb::tip() AND recovered.tip are both None, the LoopStep::ForgeTick arm forges block 0 + PrevHash::Genesis through the SAME run_real_forge -> self_accept -> SelfAcceptedHandoff path S3 proved, gated by may_cold_start_forge (recovered seed-epoch lineage present + ForgeIntent::On + FeedReason::is_forge_eligible: no_block_available | clean_empty). ONE cold-start convention: forge_header_position drives block 0 at no-tip, matching ChainEvolution::next_block_number() (the pre-S4 node_sync .unwrap_or(1) disagreement is deleted; a tip-without-height edge fails closed as RecoveredTipMissingBlockNo, never a magic default). Exactly-one is scoped to the hermetic cold-start execution (NO genesis_forged latch); the forge engine takes no ChainDb handle, so it advances no durable tip -- durable block-1+ progression is the durability slice (N-U). NO BLUE change. Tests decompose the two GREEN decisions (forge_header_position, may_cold_start_forge) + the forge_one_from_recovered(None) end-to-end path; the gate ci_check_genesis_successor_reachability.sh enforces the Option-tip signature, the cold-start Genesis emission, the single convention (no .unwrap_or(1)), the lineage+eligibility gating, and the no-ChainDb-handle structural no-durable-tip.