DC-NODE-47
DC derived partialFollowed-peer-tip possession evidence (SLICE B12). The forge-admissibility signal
(FollowedPeerTipSignal) reports the STRONGEST available evidence that the followed peer possesses a
block, not only the weakest. An ADVERTISEMENT (the peer's chain-sync tip field, absorbed by
observe() from AdmissionPeerEvent::TipUpdate) is testimony and can be stale, absent or false; a tip
the peer SERVED and Ade DURABLY ADMITTED is a demonstration, because a peer cannot serve a block it
does not have. (a) BOTH HALVES ARE HELD SEPARATELY -- latest and served -- and both are readable
on their own. (b) tip() COMBINES THEM by preferring the served half only on a STRICTLY greater
block_no; a tie at the same height with differing hashes is a fork the AO owns and falls to the
ADVERTISEMENT, so the tips disagree and DC-NODE-15 refuses with TipMismatch preserved rather than
collapsing to NoFollowedPeerTip. (c) THE SERVED FACT IS ADMIT-GATED: written only after a successful
durable admit, from the pump's own validated tip (PumpTip.slot/hash/block_no, all from the decode the
admit already performed) -- never on receipt, never via a re-read + re-decode. (d) IT IS
ROLLBACK-CLEARED: any rollback clears it and the signal falls back to the advertisement. (e) IT IS
NOT A SYNC OR CHAIN-SELECTION AUTHORITY: it never advances a tip, never feeds next_block / pump_block,
never reaches a chain selector; it may only make a forge admissible where the peer provably holds
Ade's own durable tip. (f) POSSESSION vs TESTIMONY: consumers asking whether the peer HOLDS a block
(the ForgeTick gate and the fork-switch fence -- two call sites of the SAME
forge_followed_tip_admission predicate) read tip(); consumers recording what the peer SAID (the two
convergence-evidence emit_admit_and_verdict sites, which derive an AgreementVerdict) read
advertised(). (g) IT IS PEER-BLIND, symmetrically with the advertisement half it joins; per-peer
scoping of BOTH halves is DC-NODE-35 and is owed when multi-peer follow activates.
- Source
docs/clusters/PREPROD-LIVE-2-FORGE-READINESS/SLICE-B12-served-or-advertised-peer-tip.md; docs/clusters/PREPROD-LIVE-2-FORGE-READINESS/SLICE-LIVE-2c-ACTIVATION-handoff.md (the recorded candidate fix + its 2026-08-09 supersession and 2026-08-16 discharge); docs/evidence/run-stores/preprod-live2c/b12-census-classified.txt.
- Cluster
- PREPROD-LIVE-2-FORGE-READINESS
- Introduced in
- PREPROD-LIVE-2-B12
- Authority surface
- RED/GREEN forge-admissibility signal (in-memory, non-authoritative; may only PREVENT a forge). NO BLUE change, NO durable state, STORE SEMANTICS NEUTRAL (version stays 6; neither node_sync.rs nor node_lifecycle.rs is in the store-semantics hashed surface, and the lock gate was RUN and passed unchanged).
Enforcement trace
Code
Tests 7
- the_census_frontier_tuple_resolves_caught_up_once_service_is_evidence
- a_catch_up_gap_keeps_the_advertisement_dominant_and_the_gate_refusing
- a_self_forged_tip_is_not_served_evidence_and_the_gate_refuses
- a_rollback_clears_the_served_fact_and_the_signal_falls_back_to_the_advertisement
- a_source_that_has_admitted_nothing_offers_no_served_evidence
- a_tie_at_the_same_height_resolves_to_the_advertisement_and_refuses
- all_three_venue_routes_refuse_on_the_pre_fix_census_tuple
Cross-references
Evidence notes
STATUS IS partial DELIBERATELY: the in-tree proof is complete (7 CE tests green, both gates passing, eight required mutations each breaking something) but the LIVE bar -- CE-B12-10, admitted ForgeTicks on preprod ceasing to refuse tip_mismatch and reaching leadership evaluation, closing CE-L2c-7/8/9 -- is not yet met. Flip to enforced only on that evidence. THE SAFETY ARGUMENT IS MEASURED, NOT ASSUMED: the danger the 2026-08-09 supersession named is a gate that stops enforcing catch-up, and the answer is that during catch-up the chain-sync tip field carries the peer's REAL HEAD so the advertisement LEADS and dominates. Had it instead carried the parent of each delivered header, peer_advances would read ~9,798 rather than 13; it reads 13, so across the whole catch-up the advertisement sat still at the peer's own head while service climbed underneath it, and the +1 is a property of the FRONTIER alone. Service can only ever raise the signal to a block Ade has ITSELF durably admitted, so it cannot manufacture a CaughtUp for a tip Ade does not hold. A CODE READ TURNED INTO A TEST: the LIVE-2c handoff's 'cheapest discriminator first' step 3 -- declare --participant-venue and see whether the latch fires -- is REFUTED, because participant_forge_decision returns UseInitialCatchupGate in the initial modes and the Participant arm routes that to the same dc_node_15_refusal the default arm calls, on operands bound once before the venue branch; all_three_venue_routes_refuse_on_the_pre_fix_census_tuple pins it rather than leaving it a code read, on a cluster where code reads have been wrong four times. THE CONSUMER SPLIT WAS NEARLY MISSED and is the reason clause (f) exists: both convergence-evidence sites feed derive() -> AgreementVerdict, and the combined signal folds in the block just admitted, so pointing them at tip() would make EVERY admit read Agreed by construction -- an evidence stream that always agrees, silently. They keep advertised() and are byte-identical to their pre-slice behaviour. A GATE REPAIRED IN PASSING, recorded because it is enforcement debt discovered rather than created: ci/ci_check_forge_followed_tip_admission.sh (cited by CE-B12-6) had been SILENTLY FAILING since ECA-5 (26565bec) -- its prod_body truncated at the FIRST bare #[cfg(test)], and that commit inserted an inline #[cfg(test)] async fn run_node_sync_no_eview shim above every symbol the gate inspects, so it saw an empty body and failed closed on it. Both gates now truncate at the trailing #[cfg(test)] MODULE only, and DC-NODE-15's assertions are enforced again. NOT IN SCOPE and stated so: no predicate change, no peer scoping (DC-NODE-35), no BLUE change, no store bump, no CLK, no forge-success or peer-acceptance claim. LIVE BAR ATTEMPTED 2026-08-29 AND BLOCKED, evidence docs/evidence/run-stores/preprod-live2c/b12-ce10-BLOCKED-leader-value-above-threshold.txt: two warm-start attempts on the v6 store under binary 96c7af68 emitted ZERO follow: tip lines -- the follow loop never opened, so DC-NODE-47 was never exercised live and nothing is claimed in either direction; partial stands. The halt is the separate open LeaderValueAboveThreshold defect { value: [0,3,148,36,101,52,221,219], threshold: [0,3,147,218,125,24,43,26] }, and the run CORRECTED three things about it: it is NOT catch-up-length dependent (it fires on the FIRST sync step, before one block is admitted), it is DETERMINISTIC (three occurrences byte-identical in BOTH operands across two binaries and thirteen days -- a restart does not work around it; BND-2d leg 2 stopped by SIGINT before re-reaching the wall, which is what made it look survivable), and the margin is 0.0315% not ~0.005%. The operand for that slice: the failing header is in epoch 306 = SEED + 2 (bootstrap_epoch 304, anchor slot 129,813,427 = mid-epoch-304, while epoch 306's active go snapshot is the state at the END of 304); 'the authority is a stale epoch' is REFUTED (Ade's authority is on 306, the header's own epoch). NOT a B12 regression: the rejection is header VRF validation on the RECEIVE path, the B12 signal is read only at the ForgeTick, and the identical failure predates the change. ONE THING THE RUN DID PROVE FOR THIS RULE: a v6 store written by 56e0a4e4 opened and warm-recovered under 96c7af68 with no reset and no rebootstrap terminal (anchor_before == anchor_after == 130739648/5042282/7477fc0c) and both halts left chain.db, epoch-accumulator.redb and reduced-checkpoint.redb byte-identical in size -- the STORE-SEMANTICS-NEUTRAL claim discharged LIVE, not merely by the lock gate passing.
Evidence
B12 census (2026-08-09, fcbabb67, b12-census-classified.txt): 762 admitted ForgeTicks, 6 distinct tip tuples, local_minus_peer=1 and verdict=tip_mismatch UNANIMOUS; order=pre_admit 762/762 and announcements_since_admit=0 classify the +1 as candidate (1), benign observation order.
Same census, the catch-up discriminator: peer_announcements=9798 against peer_advances=13 over that run's ~9,800-block catch-up (15m45s, one advance per ~73 s = the preprod block interval).