Hosts first, then the controller — a build and a push each, now that the mesh delivers the host. The host refuses a lower sequence than it kept and drains a batch by sequence rather than arrival; the controller numbers each send under the node's hold, inside the signed bytes. Measured: two pushes, sequence 2 in the kept declaration, counters in the store agree, no machine reads as behind. That last one is the subtlety: the mesh compares the digest of what it would send against what it did, and a number changes the bytes, so the read-only comparison composes with the last number sent rather than a fresh one.
3.1 KiB
107 — resolved: a declaration carries its order
2026-09-30. Measured on the mesh.
What was done
Hosts first, then the controller — the order issue 087 says a new declaration field needs, and now a build and a push rather than an expedition (issue 142).
The host understands a sequence on a declaration and tolerates its absence: absent reads as "no
order claimed", not "first", so a controller that sends none is still understood and a host that kept
a declaration before it understood the field compares nothing. It refuses a declaration with a lower
sequence than the one it kept, whole, and says why; and the drain that picks one declaration from a
batch keeps the highest sequence rather than the last to arrive — which is the case the report
constructed, a backlog drained out of order.
The controller numbers each send: the next number for that node, taken under the node's hold, before the body exists, so the number is inside what the mesh signs and a replayed older declaration cannot borrow a newer one's.
Measured
push shanks; push shanks
sequence in kept declaration: 2
node sequence
novox 2
shanks 2
ace (none — not sent since numbering)
g14 (none)
status: nobody "not running what the mesh would send them"
Both applies went through; neither was refused; the machine holding the earlier one accepted the later.
The subtlety, which would have read every machine as behind for ever
The mesh decides a machine is behind by comparing the digest of what it would send against what it
did send. A number changes the bytes. So the read-only comparison composes with the number the
machine was last sent — not a fresh one — and is byte for byte what was sent when nothing else
changed. Without that, numbering would have made status name all four machines as out of date on
every reading, permanently.
The open questions
- A per-node
sequenceunder the controller's node hold? Yes, as described.supersedes— the previous digest — is not added. A strictly-greater sequence gives the ordering; a chain of digests would give continuity, which nothing here needs yet and which every re-composition would break. - Genesis signing its bundle as sequence zero? Zero is "no order claimed", which is what the bundle carries by carrying nothing. Same rule, no genesis branch.
- A marker for a mode change? Not needed for the incident it guards: a replayed converged declaration reaching a node returned to adopted is already refused by mode, before this check runs.
How it is checked
Host: an older sequence is refused, a newer or equal one is not, and no order claimed on either side compares nothing; the drain keeps the highest sequence, and falls back to arrival when none is claimed. Controller: a send carries its number inside the signed bytes, an unnumbered send is byte for byte what it was before, and each node's counter is one higher per send and readable for the comparison.