Design 25: the store window, and what moving it into the server changes

The guarantee is the same and the mechanism is simpler — a nak with a
delay, no parked list, nothing lost when the controller restarts. It costs
one thing: a naked message comes back whatever happened meanwhile, so an
older report is redelivered after a newer was applied. A report already
carries the digest of the declaration it answers, so supersession becomes a
check rather than memory — ordering settled by what a message says, not by
when it arrived.
This commit is contained in:
2026-09-27 00:06:15 +02:00
parent 9946e852e1
commit 00817fb9e3
2 changed files with 38 additions and 2 deletions
+27
View File
@@ -128,6 +128,33 @@ call is a timeout the caller already handles.
Streams and consumers are objects the controller creates at genesis and asserts on start; a module
declares nothing about them. The controller is the only writer of stream definitions.
### The store window, and what moving it into the server changes
The guarantee ([ADR 0083](../../02-DECISIONS/0083-one-push-leaves-the-mesh-consistent.md)) is
that a push the controller cannot record because its store is restarting is **held and retried** —
never dropped, never falsely acknowledged. Here that is a `nak` with a delay: the server holds
the message and redelivers it, so the controller keeps no list of parked messages and one that
restarts mid-window loses nothing it was holding.
**That is a plain win, and it introduces one problem worth naming.** Holding a delivery in memory
let the controller drop an older report when a newer one for the same node arrived, because
acting on the older after the newer would undo the newer. A `nak`ed message belongs to the server
and comes back whatever happened meanwhile — so the older report is redelivered *after* the newer
was applied.
The answer was already in the message. A report carries the **digest of the declaration it is
about**, which exists because an earlier attempt to order reports by time lost the race it
invited: an apply that began under the previous declaration finishes after the next is sent, and
its report reads as newer than the send. Clocks cannot answer *which*.
So supersession stops being something the controller remembers and becomes something it checks —
a report whose digest is not the one outstanding for that node is acknowledged without being
acted on. The same shape as a node refusing a superseded declaration by sequence
([issue 107](../../04-ISSUES/107-a-declaration-carries-no-order/00-report.md)): **ordering settled
by what a message says, not by when it arrived.** And staleness is checked before the store is
waited on, so a redelivery that lost its race does not hold a slot in the window that a current
message needs.
## 4. Accounts
[ADR 0043](../../02-DECISIONS/0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md) says a