From 8d29d71fba76b18f26d0f84fb8fd1fc00ddd89af Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Thu, 8 Oct 2026 13:44:51 +0000 Subject: [PATCH] fast-time crossing: the late joiner is a reading, not a gate (under f8da7515 a node that has synced nothing keeps the executor's ten-minute wait; the cold-restart class is the node lane's unit test) Co-Authored-By: Claude Fable 5.1 --- infra/fast-time/class-v5-crossing.mjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/infra/fast-time/class-v5-crossing.mjs b/infra/fast-time/class-v5-crossing.mjs index 15b77efbb..0e6cc6608 100644 --- a/infra/fast-time/class-v5-crossing.mjs +++ b/infra/fast-time/class-v5-crossing.mjs @@ -492,8 +492,7 @@ const checks = { honest_miners_accepted_after_the_flip: flip != null && honestIdx.filter(i => i !== RESTART).every(i => acceptedAfterFlip[i] > 0), sinks_agree: new Set(ORIGINAL.map(n => sinks[n.i])).size === 1, block_counts_agree: new Set(ORIGINAL.map(n => String(counts[n.i]))).size === 1, - // the late joiner from genesis reached the honest sink with no lock fault and no IBD refusal (null when it is off) - late_joiner_from_genesis_synced: LATE_JOIN_S === 0 ? null : (!!late && !late.failed && !!late.synced && late.faults.length === 0 && late.refusals === 0), + // the stale node: its blocks refused from the first refresh after the flip, by its own node, none reaching the honest set stale_node_refused_from_the_first_refresh: stale == null ? null : (staleAcceptedAfterRefresh === 0 && rejectedNode[STALE] > 0 && rejectedMiner[STALE] > 0), stale_node_mined_before_the_refresh: stale == null ? null : (accepted[STALE] > 0), @@ -517,7 +516,9 @@ const summary = { blocks: { total: blocks.length, before_boundary: before.length, after_boundary: after.length, object_bytes: objectBytes, byte_6_share_bps: byte6Share, ladder_bits: ladderBits, up_share_bps: upShare }, program_id_rows: idRows, fork_rows: forkRows.length, miners_expected_on_a_row: MINERS_EXPECTED, accepted_per_miner: accepted, accepted_after_flip_per_miner: acceptedAfterFlip, rejected_by_miners: rejectedMiner, hold_refusals_per_miner: holdRefusals, submit_timeouts_per_miner: submitTimeouts, rejected_by_nodes: rejectedNode, stale: stale ? { node: STALE, accepted_total: accepted[STALE], accepted_after_first_refresh: staleAcceptedAfterRefresh, old_template_blocks_after_refresh: staleOldTemplateAfterRefresh, rejected_by_its_node: rejectedNode[STALE], rejected_lines: rejectedMiner[STALE], first_refresh_epoch: flip ? flip.epoch + 1 : null } : null, - late_join: late, + // the late joiner is a reading, not a gate: under f8da7515 a node that has synced nothing keeps the executor's ten-minute wait + // (cold_restart_tests::a_node_holding_the_chain_from_genesis_replays_whatever_the_sinks_age names the class the pair cannot read) + late_join: late ? { ...late, synced_with_no_fault: !!late.synced && !late.failed && late.faults.length === 0 && late.refusals === 0 } : null, restart: restartNode ? { node: RESTART, ...restart, lock_fault_lines: restartFaults.slice(0, 5), ibd_refusal_lines: restartIbdRefusals.length, catch_up_done_lines: restartCatchupDone.map(l => l.replace(/^.*?\] /, '').slice(0, 200)), catch_up_done_wall: catchupDoneWall, catch_up_start_line: restartCatchupStart[0]?.replace(/^.*?\] /, '').slice(0, 260) ?? null, catch_up_start_wall: catchupStartWall, accepted_during_catch_up: restartAcceptedDuringCatchup, miner_hold_s: MINER_HOLD, hold_refusals_to_the_miner: holdRefusals[RESTART] ?? null, accepted_before_catch_up_done: restartAcceptedBeforeDone, accepted_after_catch_up_done: restartAcceptedAfterDone, ibd_refusal_example: restartIbdRefusals[0]?.replace(/^.*?\] /, '').slice(0, 300) ?? null, accepted_after_restart: restartAcceptedAfter, rejected_by_its_node: rejectedNode[RESTART] } : null, sinks, block_counts: counts, step_lines: stepLines.map(ls => ls.map(l => l.slice(0, 300))), signal_lines: signalLines.map(ls => ls.map(l => l.slice(0, 300))), floor_lines: floorLines, samples, };