Skip to content

docs(fast-inbox): correct node documentation to the implemented behavior - #186

Draft
spalladino wants to merge 2 commits into
spl/fi-n9-leila-cleanupsfrom
spl/fi-n10-docs-corrections
Draft

spalladino wants to merge 2 commits into
spl/fi-n9-leila-cleanupsfrom
spl/fi-n10-docs-corrections

Conversation

@spalladino

@spalladino spalladino commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

Updates node-local documentation and one pinning test so they describe the Fast Inbox behavior implemented by the
stack. Twenty-two preceding implementation rungs rebuilt how this node reads the Inbox: messages are a plain ordered log addressed by
count, a block ends at an arbitrary message prefix, a checkpoint's final block resolves a live bucket
end for itself, recovery anchors on a message L1 positively re-emits inside a hundred-block window, and
every block proposal carries a signed prefix reference. The prose did not keep up. This rung is prose
only, apart from one unit test, and its job is to make the documentation describe what the code now
does.

It reconstructs the node-local half of old PR #25416 (head 0df3fa25de, commits bccabc794d through
0df3fa25de on top of 90c928ec28), and then goes further where reading the whole stack showed the
source's own text was already wrong, or wrong for this tree.

What is corrected, and why each is true here

The archiver README's two message pointers. The scanned cursor says which L1 blocks were queried;
the certified syncpoint says the whole stored log was found equal to the Inbox's position at a canonical
head. An uncertified batch advances the cursor and clears the syncpoint, and the head batch is staged
until the position after it matches the captured one. That is the model N3c built in
inbox_message_synchronizer.ts, and the README still described the single pre-cutover pointer.

The archiver README's anchor lookup. The recovery bullet claimed a five-L1-block window and an
inherited shortcut that accepted any message at or below the L1 finalized block without a lookup. FI-S12
replaced the window with messageSentSearchWindow, an inclusive [h-49, h+50] clipped at L1 block 1
and at the captured head, and N7 deleted the finalized-height short-circuit so every anchor is now a
message a bounded lookup positively placed. Neither claim survived the stack; #25416 never touched this
bullet, because upstream it did not exist in this form.

The archiver README's reorg section. It described walking backwards message by message and rolling
the syncpoint back. The rollback is now one store transaction that also prunes the proposed blocks that
consumed dropped messages (N3c), and it re-fetches unchanged messages the lookup could not place.

The sequencer README's consumption paragraph. It described the checkpoint-wide completion target:
one Inbox read resolving an end for all remaining blocks, with consumption frozen once reached. The
source carried the same stale paragraph at 90c928ec28 and #25416 never fixed it. N3b implements
90c928ec28's design instead — the endpoint is resolved per block, nothing is retained between blocks,
a non-final block takes the further of the resolved endpoint and the safe local step, and only the final
block lands exactly on a boundary or the checkpoint is abandoned. FI-S11 later made that final block
reuse the range already read at the endpoint.

The validator README's checkpoint validation list. It stopped at the archive-root check, omitting
the checkpoint consumed-message content check N3a added and the L1 endpoint gate N6 added, both of which
run before an attestation. Both are now listed, the gate described with N6's actual policy.

Node public-call prediction. The class and method documentation called the appended bundle a lower
bound on what the next block consumes. It is not a bound in either direction: a final block lands on a
live bucket boundary that can sit below the local step, so a call consuming a message the simulation
covers can fail for real. cb5b03d5bb made exactly this correction and it holds here, since N3b's
selection is what the node reuses. The checkpointStartBlock parameter doc was also wrong — it is the
last block of the parent checkpoint, not of the checkpoint being extended, as the code comment four
lines below it already said.

selectSafeLocalEnd and the automine comment say the same thing from the sequencer side: the
qualifier that only a non-final block may fall back to the safe local step, and that landing on a
boundary can leave observed messages behind.

A unit test in inbox_message_selection.test.ts pins the concrete case the prose describes: cursor
at 0, 400 messages observed, live buckets ending at 200 and 400, safe local step 256, final block ending
at 200, message index 220 covered by the estimate and never inserted.

The bucket e2e header comment. It claimed co-timestamped anvil blocks were the only way a bucket
could span two L2 blocks. Since N3b, an ordinary block can stop anywhere inside a bucket it observed
whole; what the co-timestamped blocks actually buy those two scenarios is a bucket still open across the
split and a bucket growing past an already-selected endpoint.

Range-read failures across JSON-RPC. InboxMessageRangeNotSyncedError and the
getL1ToL2MessageRange interface doc now state that the class does not survive a JSON-RPC hop and that
callers must not branch on instanceof. FI-S08 already depends on this: its
EXPECTED_RANGE_READ_FAILURES classifier matches message text for precisely this reason.

The kv-store range contract. end was documented as "the key of the last item to include", which is
wrong in both directions. Going forwards the range is [start, end); going in reverse the inclusivity
flips to (start, end]. The source's correction flatly called end exclusive, which would have been
wrong here, so the wording states the direction dependence instead. It is pinned by
kv-store/src/interfaces/map_test_suite.ts and already documented in the sqlite-opfs backend.

Stale claims deliberately not copied

  • Twice-refreshed endpoint checks. N6 built the simplified policy: read the L1 block number once,
    resolve the bucket pinned to that height, and do not re-read the block hash afterwards. Any prose
    describing a second refresh would be false here.
  • Neutral inactivity accounting. That is deferred work (unverifiable) and is not implemented. N6
    maps its two new reasons onto the existing unvalidated outcome, and the baseline sentinel still
    counts an unvalidated proposer duty against inactivity. Nothing in this rung claims otherwise.
  • A 101-block window. FI-S12 built an inclusive [h-49, h+50] — exactly 100 heights before
    clipping, clipped at L1 block 1 and at the captured head. The README says 100.
  • Bucket-lag behavior. N3b and N3c removed the bucket model from the node entirely; it survives only
    on L1's own surface. No prose here describes the node tracking bucket lag.

Deliberately out of scope

  • validator-client/src/proposal_handler.ts: #25416's JSDoc-reordering hunk is already applied — N3a
    moved that comment back onto awaitCheckpointConsumedMessages when it ported 497132c332.
  • docs/docs-developers/.../migration_notes.md and docs/docs-operate/.../changelog/v6.md
    (8f2d79bfc7) are owned by aztec-packages.
  • The regenerated aztec.js reference (0df3fa25de) lives in the docs site, not here.
  • spartan/, root docs, Noir, L1 and bb files from old #25416.
  • No behavior changed. Where reading the stack suggested a code change rather than a prose change, it
    was recorded rather than made — see below.

Stack position

This is rung 23 of 25 in the Fast Inbox node stack, stacked on spl/fi-n9-leila-cleanups.

Tests

The diff is documentation and comments in every package except sequencer-client, which gains one unit
test. Red/green does not apply to prose and was not fabricated.

  • yarn build: green. yarn format and yarn lint: clean.
  • @aztec-labs/sequencer-client full suite: 12 suites, 306 passed / 1 skipped (base 305/1, so
    exactly the one added test). inbox_message_selection.test.ts alone: 18/18 (base 17).
  • @aztec-labs/kv-store test:node: 289 passed, at base parity — the change there is a JSDoc, run only
    to confirm the direction-dependent bound semantics the new wording asserts.
  • archiver, aztec-node, aztec.js, stdlib, validator-client and end-to-end changes are
    comment- or README-only; their suites were not re-run for show.
  • No e2e run. The only e2e file touched is a header comment.

Recorded, not fixed

The new unit test is a pin, not red/green evidence: no production code changed, so it passes before and
after by construction. It would redden if selectSafeLocalEnd started consulting L1 or if
resolveEndpoint stopped clamping a final block to the newest boundary at or below its bound.

Describes what the streaming Inbox node actually does now that the bucketless
cutover, the authenticated anchor search and the required prefix reference are
all in place.

The archiver README separates the scanned cursor from the certified syncpoint,
states that an uncertified batch clears the syncpoint rather than advancing it,
and rewrites the reorg section around the anchor rollback. Its recovery bullet
loses two claims the code no longer supports: the lookup window is 100 L1 blocks
around a message's recorded height, not five, and no anchor is accepted without
a positive lookup.

The sequencer README drops the checkpoint-wide completion target: an endpoint is
resolved per block, a non-final block takes the further of that endpoint and its
safe local step, and only the final block has to land exactly on a boundary. The
validator README lists the checkpoint content check and the L1 endpoint gate that
now run before an attestation.

The node's message prediction is described as an estimate in both directions
rather than a lower bound, with the case where a final block lands below it, and
`selectSafeLocalEnd` and the automine comment say the same. A unit test pins that
case. The two aztec.js readiness helpers say they answer about the chain as it
stands, which is what a caller should use when a simulation is not enough.

Range failures from `getL1ToL2MessageRange` are documented as untyped across
JSON-RPC, and the kv-store range contract now states that bound inclusivity flips
with direction.
@spalladino
spalladino added this pull request to stack #188 September 12, 2026 04:52
… jsdoc

The node documentation corrections rewrote the jsdoc on waitForL1ToL2MessageReady
and isL1ToL2MessageReady, but the committed auto-generated aztec.js reference was
not regenerated, so docs/bootstrap.sh's staleness check failed.
@spalladino spalladino added the ci-draft Run CI on this draft PR label Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-draft Run CI on this draft PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant