Designs 25, 51: bound the race retry to a settled heard after the pull started
mesh/delivery merged: source.main moved to novox/hq@6cd965b3cdf6
mesh/check being carried to 4f6b9f0ca5ff on 6cd965b3cdf6 | from e7ac664afff7

A retry on any settled ever heard would apply again on every failure; the
pull's start bounds it to one retry per settled, kept in memory. The seat and
holder beside a routed image are outside a container's digest, so naming
them replaces nothing, and the verdicts gap case is named.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 02:16:03 +01:00
parent a838b6b3d3
commit 4f6b9f0ca5
2 changed files with 12 additions and 7 deletions
+1 -1
View File
@@ -1012,7 +1012,7 @@ issue 1966.
| The bus module creates, and the binding follows | live, by tool call: a module asking for a state, a log, objects and a ledger in `requires` receives each binding in its membership, written after `created`, never before; `show` gives each entry's template, settings and fill; a module calling `create` is refused |
| The bus module runs whole | one integration test in the nats module's repository that starts the bus module whole against the real bus server: a verdict, then `created`, then the binding in the membership; a restart that catches up a verdict given while it was down; a bucket whose settings differ raises `drifted`, the seat's `apply` of a larger cap clears it with `drift-cleared`; a refused create raises its condition and hands no binding; `recreate` and `drop` without the operator's warrant are refused, and with it act after a stream snapshot; an orphan is listed, raises `orphaned`, and goes only by `drop`; each template at a small cap refuses, events drop the oldest, and `full` is raised at the raise mark and cleared only below the clear mark; `recreated` rewrites the binding and `dropped` removes it; at start the first view from `indexer_bus_state` creates what a verdict given while it was down asked; the older remove tools refuse a new-form entry, naming `drop`'s tool |
| A module asking bus state waits for the bus module | the module graph's table test: a manifest with a ledger entry has a requirement edge to the module providing `bus-state`, one without has none (ADR 0614 §5) |
| A foundation part's first view | unit tests of each owner and reader (ADR 0615's How it is checked), as table cases, and these cases of the whole-module integration tests that exist today (mesh-delivery is over its integration budget, so no new test function): the indexer's (`modules/mesh-delivery-indexer`, its one whole-module test, which the milestone's packages test merges into), the assigner's (`modules/mesh-delivery-assigner`) and the grants part's (`modules/mesh-grants`), and the tool runner's in mesh-tools: the owner answers its verb at once from what it holds, with its revision, before its own first view; the reader asks once at start, then only on a gap in revisions; a reader whose owner is down raises its condition only past the bound and never reads the owner's state, and the owner's answer clears it; an empty answer at revision 0 drops no login; an owner's event carries the changed value whole, and a reader applies a put and a delete from the events alone and never asks the verb; a revision lower than the one held makes the reader ask its first view again; an existing waiter on `manifest-accepted` or on the assigner's `put` does not wake on the new families; for the indexer's `verdicts` (ADR 0622), called by the grants part while the node's tool runner is stopped, it answers, and from any other account, the indexer's own included, it is refused; the grants part and the indexer started at the same moment, `verdicts` answers within the bound with its revision; with the indexer down past the bound the grants part raises the condition naming the indexer and `verdicts` and never reads the indexer's state, and the answer clears it; the read tool shows the revision each reader holds, and the re-read tool asks once and records the hand act; in the
| A foundation part's first view | unit tests of each owner and reader (ADR 0615's How it is checked), as table cases, and these cases of the whole-module integration tests that exist today (mesh-delivery is over its integration budget, so no new test function): the indexer's (`modules/mesh-delivery-indexer`, its one whole-module test, which the milestone's packages test merges into), the assigner's (`modules/mesh-delivery-assigner`) and the grants part's (`modules/mesh-grants`), and the tool runner's in mesh-tools: the owner answers its verb at once from what it holds, with its revision, before its own first view; the reader asks once at start, then only on a gap in revisions; a reader whose owner is down raises its condition only past the bound and never reads the owner's state, and the owner's answer clears it; an empty answer at revision 0 drops no login; an owner's event carries the changed value whole, and a reader applies a put and a delete from the events alone and never asks the verb; a revision lower than the one held makes the reader ask its first view again; an existing waiter on `manifest-accepted` or on the assigner's `put` does not wake on the new families; for the indexer's `verdicts` (ADR 0622), called by the grants part while the node's tool runner is stopped, it answers, and from any other account, the indexer's own included, it is refused; a missed revision on `manifest-refused` makes the grants part ask `verdicts` once more; the grants part and the indexer started at the same moment, `verdicts` answers within the bound with its revision; with the indexer down past the bound the grants part raises the condition naming the indexer and `verdicts` and never reads the indexer's state, and the answer clears it; the read tool shows the revision each reader holds, and the re-read tool asks once and records the hand act; in the
| The owners' first views are their own tools, in a runtime of their own (ADR 0621) | cases of the indexer's and the assigner's whole-module integration tests: each first-view tool, called from a reader's account while the node's tool runner is stopped, answers (§1); each answers its granted readers and refuses every other account, the owner's own included (§2); the two runtimes started at the same moment, each asking the other's first view at start, both answer within the first-view bound with their revisions (§3); and the milestone's group passes its merge check with no change to the judge's known fields for these verbs (§4) |
assigner's whole-module test, the assigner starts before any new-form bus state exists, with no binding of the bus
module, and the indexer, the grants part and the tool runner each get their first view of it. Live, after the delivery: the indexer's `indexer_old_sections`, which the milestone extends to count the `reads` entries on each owner's state, reads 0 declared `reads` entries on the indexer's state and 0 on the assigner's, and says the node-engine's reads granted in the grants part's code remain (§ 12.6); and each owner's read tool of § 12.2 answers what its bucket holds, each status view tool answers a session's call as before the move, and `mesh_search` finds each owner's read tool |
@@ -204,13 +204,16 @@ the verb.
healthy (design 47), is blocked by nothing else. `distribution` claims a seat held once for the mesh and runs on
one node, so the node-engine on every node hears `settled` for the store's holder, on whichever node the holder
runs, and then applies once more each module on its own node whose have says failed naming an image of that store,
said in its journal. **No `settled` is missed in a race**: when the node-engine records a pull failed against a
store, it checks whether the holder's latest event is already `settled`, and if so applies again at once, so a
pull that failed just before the holder settled does not wait for the next one. It finds the holder by the seat the failed image came through, read from its
said in its journal. **No `settled` is missed in a race**: the node-engine keeps in memory the last `settled` it heard
for each holder since its own start, never reading the status view's state (ADR 0596). When it records a pull
failed against a store, it compares the pull's start with that event's time: only when a `settled` for that
holder was heard after this pull started does it apply again at once. That is the one retry, once per `settled`: a
second failure after the same `settled`, or the at-once retry failing again, starts no further apply. It finds the holder by the seat the failed image came through, read from its
declaration, never by a module's name: **the composition writes, beside each image reference it routes, the seat the
reference came through and that seat's holder**, taken from the holdings the composition already has, so the retry
matches `settled` by the holder its declaration names. A change of the seat's holder composes the declaration
again, so the name it carries is the holder of now. **The seam that changes is the grants part's list of what a node-engine
again, so the name it carries is the holder of now. The seat and the holder are not part of what a container is
(they are not in its digest), so a declaration composed again for them replaces no container. **The seam that changes is the grants part's list of what a node-engine
hears** (`NodeAgentHears`; the node-engine declares no `consumes` of its own): it gains one entry, the status
view's `settled` family, which the node-engine filters for the holder of the seat its failed image names. **At its
own start** the node-engine also applies once more every have that failed naming an image of a store, so a restart
@@ -489,13 +492,15 @@ test).
module whose pull failed, its have failed naming an image of a store, is not applied again with the clock moved
past the bus backoff's four tries and no event heard; it is applied once more, and its new image pulled, on a
`settled` for the holder of the seat its image came through, a `settled` said for the holder on another node
included; a pull that fails after the holder's latest event was already `settled` is applied again at once; a
included; a pull that fails after a `settled` for its holder heard since that pull started is applied again at
once, while a `settled` heard before the pull started applies nothing at once, a second failure after the same
`settled` applies nothing, and a race retry that fails again starts no further apply; a
`settled` of another module applies nothing; at the
node-engine's start it is applied once more with no event; a module being applied when `settled` arrives is not
applied twice; a retry whose pull fails again says failed again and starts no further apply.
- **Node-engine's composition, unit** (cases of its composition test): a declaration's routed image reference names
the seat it came through and that seat's holder; a change of the seat's holder composes the declaration again with
the new holder named.
the new holder named and recreates no container.
- **The grants part, unit** (a case of its composition test): a node-engine's subscriptions hold the status view's
`settled` family beside the backup holder's word, and nothing more.
- **Checker.** A changed build module invoking a verb no running holder serves is not run, and the verdict names the