Repository navigation
refactor(node): feed a dispute ruler only the window it was positioned at - #302
Merged
Merged
Conversation
GCdePaula
force-pushed
the
fix/node-single-window-dispute-feeder
branch
from
October 19, 2026 17:11
8af7824 to
596d1b0
Compare
GCdePaula
marked this pull request as ready for review
October 19, 2026 17:12
stephenctw
previously approved these changes
Oct 20, 2026
The whole-epoch differentials (the echo golden epoch_root_r44_h48, the runner's root replayed under both tables, and the corpus mcycle cases) asked the dispute facade for a level-0 root on a fresh store. With no runner rows to serve it, the facade replayed every input window on the dispute feeder, which stored each crossed boundary in full. Production never does that: the runner's rows serve those levels. engine_level_root now replays a level wider than one window on the scratch feeder, generalizing scratch_quartet_root to any stride, so the runner's eager fold is still checked against an independent machine replay. Levels inside one window keep the facade and both collectors; engine_root goes through the same dispatch.
No test fed a rejected input from a positioned boundary above the epoch start, so no test reloaded a directory the crossing had published as the revert checkpoint. Echo rejects input 2: a middle-level root over window 2 crosses inputs 0 and 1, publishes boundary 2, feeds input 2 from it, and must match the scratch replay and end idle on boundary 2's state.
…d at The dispute feeder committed every boundary it crossed into the store, fully storing any the store missed, and used that directory as the revert checkpoint. Since the torn-boundary fallback became a stop, no production ruler feeds past the window its boundary was loaded from: positioning crosses on clones and publishes the target, nested levels and level-0 quartets below window height lie inside one window, and the frontier serves the rest. An instrumented run saw 467 positioned rulers and no production feed beyond the positioned window. The feeder now keeps that window and the directory positioning loaded and verified, uses it as the revert checkpoint as the advance feeder uses the batch's, and asserts loudly if asked to feed any other window. Its constructor becomes private. snapshot_resumed_source_matches_template_replay fed two windows through the feeder directly; what it compared, a source resuming from a crossing-published boundary 1 against a template replay, stays pinned by restarted_source_resumes_a_half_built_level, and the window-1 tree shapes by dispute_source_matches_prototype_tree. Tests and docs that described a write-back now describe the crossing.
commit_boundary_machine and stage_machine_store lost their last caller with the single-window dispute feeder. Production no longer stores a machine in full: the runner and the crossing publish clones through commit_clone and publish_staged, which stay. Their storage tests go too: the write-once row panic stays pinned by insert_boundary_disagreement_panics, the vanished-row skip by nearest_boundary_answers_the_floor_and_self_heals, and republication by a_vanished_dispute_boundary_is_crossed_to_again. The startup sweep still removes .part-* leftovers from older versions.
The frontier comment still promised that a store the runner never processed is served by full-epoch replay. With the single-window feeder the machine answers only quartets that feed at most one input; production never disputes such a store with inputs, since a hero starts only after the runner's roll, and the engine harnesses replay wider levels on the scratch feeder.
GCdePaula
force-pushed
the
fix/node-single-window-dispute-feeder
branch
from
October 20, 2026 09:36
596d1b0 to
7becfb0
Compare
stephenctw
approved these changes
Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #301, now merged; this branch is rebased onto main.
Why
#301 turned a torn dispute boundary into a stop. That removed the last
production path into the dispute write-back's content-addressed miss, the
branch that stored a crossed boundary in full. Today every production
dispute feed lands on the window its boundary was loaded from:
window;
An instrumented run saw 467 positioned rulers and no production feed
beyond the positioned window. The write-back's only remaining callers were
tests that replay whole epochs on a fresh store.
This PR makes that contract explicit before the scoped review of
engine/{ruler,machine_stf,dispute}.rs, so the reviewer reads one feedercontract instead of two.
Reading guide (commit by commit)
engine_level_rootdispatches on the level's span. A level wider thanone window (the echo golden
epoch_root_r44_h48, the runner's rootreplayed under both tables, and the corpus mcycle cases) now replays
on the scratch feeder.
scratch_quartet_rootis generalized to anystride for this. Golden values are unchanged.
new coverage. Echo rejects input 2. A middle-level root over window 2
reverts to the directory the crossing published, matches the scratch
replay, and ends idle on boundary 2's state. No test fed a rejection
above boundary 0 before.
at. This is the contract change.
Feeder::Storekeeps thepositioned window and the directory
ruler_atloaded and verified,and uses it as the revert checkpoint, as the Advance feeder does with
its batch checkpoint. Feeding any other window panics. The constructor
is renamed
at_stored_boundaryand is private. Two other changes:snapshot_resumed_source_matches_template_replayis deleted. Itdrove the write-back directly, and what it compared stays pinned by
restarted_source_resumes_a_half_built_levelanddispute_source_matches_prototype_tree.crossing. In
node-architecture.md, the reason not to skip a tornboundary is now the single-window feeder.
removes
Storage::commit_boundary_machine,stage_machine_store, andtheir four storage tests. After this commit, production never calls
cm_store.publish_staged,checkoutandcommit_clonestay, sincethe runner and the crossing use them.
frontier comment in
dispute.rsstill promised full-epoch replay froma store the runner never processed; it now says the machine serves only
quartets that feed at most one input, and that wider levels replay on
the scratch feeder in tests.
Net production code: -62 lines (
snapshots.rs-67,machine_stf.rs+5).Tests: -117 lines of storage unit tests, -28 in
engine_machine.rs.What to scrutinize
Feeder::Store. If the root stridecould ever exceed one window, or a production ruler fed past its
positioned window, this assert is the loud revisit trigger. Before this
PR, that case was a silent full store.
sync and no rehash.
restore_rejectedstill asserts the reloaded rootequals the revert root.
Gates
All run in the emulator v0.22 devshell, on v0.22 echo and yield images (archive_version 8), on the tree this branch has now (the rebase onto main left it byte-identical):
just check: green. The node lib has 310 tests: fix(node): robustness pass from the Go node and feature/prt comparison #301's 314 minus the four deleted storage tests.just test-engine-machine: 22 passed.just test-node-harness: 28 passed.computation_hash_corpus_dave_matches_release_manifest, release, against the v0.22.0 corpus): passed.just e2e-smoke(TEST_INSTANCE=18619): 0 of 5 failed: echo simple, chaos, kill_catchup_batched and stf_all; and yield stf_revert, whose dispute over the revert transition the honest node won.