Merge pull request 'Design 47 milestone G: develop answers batches 6 and 7 (G-16 to G-18, §587-§705), the operator's answers O-1 to O-9 of 2026-11-11' (#552) from design/47-G-develop-answers-6 into main

This commit was merged in pull request #552.
This commit is contained in:
2026-11-11 20:09:06 +00:00
19 changed files with 1487 additions and 105 deletions
+3 -2
View File
@@ -976,8 +976,9 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564, 0668.
(ADR 0235).
- **recovery module** — the holder of the seat `mesh-recovery`, held once for the mesh: under an account of its own it
takes a restore point of a module's kept data on the live bus before the module moves across a `form` change, sets
the live data aside and restores the point at a revert, and lists and removes restore points; the bus module and
the snapshot account restore nothing into the live bus, and the backup holder only stores copies (design 47 G §554).
the live data aside and restores the point at a revert, and lists and removes restore points; the snapshot
account restores nothing into the live bus, the bus module only the stream snapshot it took before a `drop` or
`recreate` of a protected stream, in place (design 47 G §681), and the backup holder only stores copies (design 47 G §554).
**aside** — live kept data a restore set aside, removed only by the hand tool `recovery_discard_aside`, which the operator and sessions call.
*Not:* ~~restorer~~, ~~restore account~~
- **disk readings** — the size of each data item a node's modules declare, measured by its `measure` and kept
@@ -126,8 +126,8 @@ that holds its mark reach the machines together.
0670 §2); gains `operator-channel` in `uses`, for the asks of marked waits (§35; design 46 7-approval-mark §7); gains
in its old `state` section the per-machine state `approvals` of design 46 7-approval-mark §13 (the tool runner is a
foundation part, ADR 0619 §2, §3); and gains the stream `node-tools-findings` and
the `consumes` of `mesh-conditions.findings-asked` that design 47 G §387 writes. Its `version` stays `2`, a label
no reader judges. Nothing else in it changes.
the `consumes` of `mesh-conditions.findings-asked` that design 47 G §387 writes. Its `version` is `0.1`, as design 47 G §368
and §624 have every manifest's. Nothing else in it changes.
12. **The publication time is kept.** `bus.Heard` gains `Published`, read from the event's header `x-time`, which
mesh-sdk's emit sets (`go/process/emit.go`) and the status view's own emit too; `HearFirst` reads it from the
message's headers, and leaves it empty for an event that carries none.
@@ -185,8 +185,9 @@ that holds its mark reach the machines together.
`bus/wait_test.go`, `TestTheBusRefusesAWaitOutsideTheGrant` stays as §16's refusal test, with a timeout of a
fraction of a second, its comment citing ADR 0670 §1. The manifest test also holds each change of §11: no `waits`,
`operator-channel` in `uses`, the state `approvals` in the old `state` section, per machine with its time to live,
and none in `requires`, and the finding stream. New
test functions are allowed within the module's budget of 500 unit and 50 integration tests (Q-TB).
and none in `requires`, and the finding stream. The
tool runner ends milestone G with no more tests than it starts with (design 47 G §419, §626): these tests replace
existing ones, and every test deleted counts.
## The claude-code module's Stop hook
@@ -319,8 +320,8 @@ approves, for a marked event or a restricted stream.
`from: seat` is never marked.
35. **A marked wait runs only on the operator's warrant for that one wait.** The call is an act as design 46
7-approval-mark §8 makes one: the address `mesh_wait`, the node, the module `node-tools` and the tool `mesh_wait`,
its build (the tool runner's own fingerprint, as the node-engine's `running.<node>.node-tools` last said it, which the
tool runner hears, design 47 G §309; Q-BD; while it is unknown, a marked wait is refused, saying so), and every
its build (the tool runner's own fingerprint, read from its `MESH_FINGERPRINT` at its start, design 47 G §205 and
§625; while it is unknown, a marked wait is refused, saying so), and every
argument given (`subjects`, `from`, `filter`, `timeout`), so the warrant binds the exact names, the filter and the
bound. The tool runner refuses, asks, judges and claims it as that part's §9, §10, §11 and §13 to §17 do for a
marked tool, asking on the seat `operator-channel` under its own module's name, `node-tools`, whose manifest gains
@@ -379,8 +379,8 @@ enter; `node-files.stat` answers such a file's mode from this milestone (§21a),
restart between the run and `warranted`'s answer. The client's `KeyPrefix` (§10) has a row in mesh-contracts'
client test: every key of its own `State` written and read carries it, an empty prefix writes today's keys, and
with a prefix set `Where` still reads `<module>.<id>`. The mesh MCP server's `TestMeshWaitIsAnnouncedAmongSeven` counts eight
tools with `mesh_approvals` (Q-T). New test functions are allowed within each module's budget of 500 unit and 50
integration tests, and a module over it shrinks toward it with each change (Q-TB). *Why:* the operator's test
tools with `mesh_approvals` (Q-T). In milestone G a module ends with no more tests than it starts with (design 47 G §419, §626): these tests replace
existing ones, and every test deleted counts. *Why:* the operator's test
budget (one whole test over many small ones). *How it is checked:* the check counts each module's tests against
its budget.
23. **In G's delivery group, ordered by the module graph.** The members are pushed and checked in design 47 milestone
@@ -4,6 +4,9 @@ status: in-progress
code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-catalog modules/gitea, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, mesh-contracts, mesh-tools, mesh-networking, hq 00-META/checks]
updated: 2026-11-11
decisions:
- "ADR 0673"
- "ADR 0672"
- "ADR 0668"
- "ADR 0667"
- "ADR 0666"
- "ADR 0664"
@@ -153,7 +153,7 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l
in place of `source.main` (ADR 0429 §11 to §13 as ADR 0666 §6 changes them), in the old `state` section `source`
already holds, its key's layout being the only change. The gate is no foundation part: the bus module's delivery
waits on no act of the gate's, since the indexer takes a revision in from the forge bridge's stream and the assigner
writes wants from the runner's events, so the gate's other kept data stay in ADR 0595's templates (ADR 0666 §4), and serves it by the seat
writes wants from the runner's events, so the gate's kept data are as §613 has them, state by state (ADR 0666 §4), and serves it by the seat
`mesh-delivery`'s verb `source` with the branch, saying `source-moved` on each new revision; the indexer and the
node-engine's catch-up read it there, never the gate's state (ADR 0596). Each want names a build-input fingerprint
computed at one such revision.
@@ -256,7 +256,7 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l
revision the runner says one `group-ended` once every member's modules have ended, and the assigner writes the
group's wants only on it: a member's own `pipeline-ended` writes nothing (ADR 0666 §9).
- §30 **A failed `optional` step holds nothing.** It is shown in `pipeline-ended`, in the delivery record and in the runner's
`show`; the operator may accept it by the runner's verb `accept` (§394), recorded in the hand-act log with who and why.
`show`; the operator or a session may accept it by the runner's verb `accept` (§394, §610), recorded in the hand-act log with who and why.
- §31 **A newer commit supersedes an older pipeline of a module whose fingerprint it changed.** On a newer commit of the
same watched branch the runner ends a module's older running pipeline as `superseded` only when the newer commit
changed that module's build-input fingerprint (ADR 0666 §9); a module the commit leaves as it was finishes its
@@ -117,11 +117,11 @@ each the design's choice with its reason, §468 to §479 those of the runner's,
says no set; §383's and §384's checks.
- §462 **Where the bus state calls and their capabilities land.** (§488) The calls' names and shapes are in mesh-sdk's
`go/buscalls`, which holds only those (its package's own rule): §408's ledger calls are `mesh/ledger.fill` (one
ledger's fill against its cap, as `buscalls.Fill`) and `mesh/ledger.remove` (one instance removed with its
operations and its snapshot, §155), and §407's `mesh/objects.sent-get`. The capability each belongs to is listed
ledger's history's fill against its cap, as `buscalls.Fill`, §587) and `mesh/ledger.remove` (one instance removed with its
operations and its snapshot, §155, its request and answer §587's), and §407's `mesh/objects.sent-get`. The capability each belongs to is listed
where the capabilities are today, in the tool runner in mesh-tools (its families of calls per capability): the two
ledger calls under `ledger-calls`, `mesh/objects.sent-get` under `bus-state-calls`. mesh-sdk gains no capability
list. This amends §347 ("mesh-sdk lists") and §408. Why: the tool runner states the capabilities of the calls it
list of families; it names the words (§622). This amends §347 ("mesh-sdk lists") and §408. Why: the tool runner states the capabilities of the calls it
answers (ADR 0595 §9) and the shapes alone are shared, so a module importing the calls rebuilds nothing of the tool
runner's (develop question D-17). How it is checked: unit (tool runner), its table of families names each new call
under its capability; unit (`buscalls`), each request and answer read.
@@ -295,9 +295,8 @@ reason. What a question of another kind leaves open waits for the operator and i
`wait-held` with `step: delivery` (§498) and `version-delivered`'s machines away. Why: §186 has the tiers worked out
from the difference and the owners' waits (develop questions VW2, VW3 and on §431). How it is checked: test 45; test
106 for the delivery wait.
- §513 **A state's objects store has a cap of 256 MiB, and the binding is mesh-sdk's.** (§607) The bus module plans each
`<state>-objects` (§58) with a cap of 256 MiB, as a sent store (§477), unless the state's `requires` names
`objects-max-mib`. The binding's `objects` and `sent-objects` (§399) are fields of mesh-sdk's
- §513 **A state's objects store has a cap, and the binding is mesh-sdk's.** (§607, §547, §591: the cap is §591's) The bus module plans each
`<state>-objects` (§58) with the cap §591 names, unless the state's `requires` names `objects-max-mib`. The binding's `objects` and `sent-objects` (§399) are fields of mesh-sdk's
`membership.BusBinding`, built in mesh-sdk. Why: the objects template has no default (develop questions NATS1 and
on §399). How it is checked: unit (bus module's plan), each planned objects store has its cap; unit (membership),
both fields read.
@@ -306,7 +305,7 @@ reason. What a question of another kind leaves open waits for the operator and i
manifest's `invokes`, with `every-channel: true`, which the router sends on every channel's `send`, as design 46
announces a break-glass act. The words are the operator's to approve and wait for them (develop questions TL1 to
TL3). How it is checked: whole (teller, router), each of the three reaches every channel once.
- §515 **The condition keeper's sources are its consumers' bindings.** The keeper learns that the grants part made a
- §515 **The condition keeper's sources are its consumers' bindings** (corrected by §663: the bus module's `created`). The keeper learns that the grants part made a
source module's durable consumer (§386) from the seat `mesh-broker`'s binding of that consumer to the keeper (§399),
heard as its event and read by `list` at the keeper's start, and asks `findings-asked` naming the module on it. At a
start with no kept sources and no first view of the catalog half, its sources are the modules its consumers'
@@ -255,7 +255,7 @@ that round are written into the lines they name, here. Questions that an earlier
finding raised before their move is still raised after it.
- §540 **The keeper hands a failed head to the operator after two hours.** The condition keeper raises
`pipeline-failed` (§33) with `owner` the commit's author, as the runner's finding names it, and two hours after the
finding's `since` sets the condition's `owner` to `operator-channel`, the seat the operator is asked through (a
finding's `since` sets the condition's `owner` (a field of the condition, §685) to `operator-channel`, the seat the operator is asked through (a
seat's name, §451), and says that change as it says any change of a condition; the runner's finding keeps the
author. §451's `owner` and `waiting-on` gain a third form, `forge:<account>`, the commit's author as the forge
names it. A `pipeline-failed` for a newer failing head is a finding of its own, its id naming that commit, so it is
@@ -277,7 +277,7 @@ that round are written into the lines they name, here. Questions that an earlier
on `wait-ended`; a `composed` whose fingerprint is not the want's is covered by that same wait or by the put's
wait (§498).
- **The healers' attempts.** The seat `mesh-healers` says `attempt-changed` on
`mesh.seat.mesh-healers.event.attempt-changed.<condition>`, the condition's key escaped as a key's name is (§73),
`mesh.seat.mesh-healers.event.attempt-changed.<condition>.<attempt>` (§665), each token escaped as a key's name is (§73),
carrying the attempt's value whole as its key in `attempts` holds it and `until`, the end of that key's
lifetime, or `removed: true` when the healer ends the attempt; the seat's definition, in its contracts module,
keeps it last per subject, 7 days and 16 MiB (§396). The healers say it again for every open attempt at their start. The
@@ -365,7 +365,7 @@ that round are written into the lines they name, here. Questions that an earlier
checker's old Node form goes with it: it no longer packs a group member's package for a Node fetch (ADR 0500 §5),
for any group, since a group's builds against its members' heads are the runner's (ADR 0573 §4). The checker's
manifest of G's heads consumes neither package registry's `published` and its asks stream carries no `tag-wanted`;
its `go.mod` requires nothing that no code of it imports; and the checker's seat definition in mesh-contracts
its `go.mod` requires nothing that no code of it imports; and the checker's seat definition in mesh-delivery's contracts (§645)
defines no `tag-wanted` holder event, which goes as §321 retires `tag-wanted`. Until G's
delivery the running checker makes G's own check with main's code (ADR 0667 §2). Why: §66 and §373 (develop
questions CK10 and the checker developer's three after it, batch 5). How it is checked: the code review finds no
@@ -504,12 +504,12 @@ that round are written into the lines they name, here. Questions that an earlier
is the seat `mesh-module-assigner`'s state. The state is an old `state` section of the builder, a foundation part,
one key per node and language, about 12 keys of about 0.3 KiB each, capped at 1 MiB, so it adds no objects store
to §607's count (§58 plans one only for a state of the template); `builder_toolchains` reads it and
`builder_toolchains_drop`, of class `act` with §391's default callers (`operator`, `sessions`), deletes one key,
`builder_toolchains_drop`, of class `act`, taking its default (§610), deletes one key,
with why, written in the hand-act log of the seat `mesh-hand-acts`; a
holder says the key again at its start and on each change (§492). This amends §604's "when the node leaves the
catalog's placements". Why: the builder reads no other module's state (ADR 0596 §2; round 3, seams and tools: R4).
How it is checked: whole (builder), a build module stopped on a node deletes that node's key, and the drop tool
deletes one key and logs it; a drop by another caller is refused by the bus.
deletes one key and logs it.
- §547 **The sum of the bus's caps is read against its limit.** Measured on 2026-11-11 against an embedded bus
(nats-server 2.11.17): an account's storage limit reserves each stream's maximum size when the stream is made (a
second stream of 64 MiB under an account limit of 100 MiB is refused, "insufficient storage resources"), and a
@@ -77,7 +77,7 @@ takes §600 on, since §516 on are those of batch 4's file G-12.
- §604 **The builder keeps the toolchains it heard.** (§546) The builder writes each `toolchain-reported` (§492) in a state of
its own, `toolchains-heard`, one key `<node>.<language>` holding the version, the recipe, the digest and when it
was said, and agrees the version from those keys, read back at its start, so a restart needs no node to report
again. Its manifest `consumes` the three build seats' `toolchain-reported`; its tool `builder_toolchains` lists the
again. Its manifest `consumes` the Go and node build seats' `toolchain-reported` (§702); its tool `builder_toolchains` lists the
keys; a key is deleted when the node leaves the catalog's placements (§365's reason). Why: a durable consumer does
not deliver an acknowledged event again (review round 2, seams and tools 1). How it is checked: whole (builder), a
restarted builder agrees the same version with no new report.
@@ -41,14 +41,14 @@ are written into the lines of G-12 they name.*
- §554 **The recovery module takes, sets aside and restores kept data on the live bus.** The operator,
2026-11-11 12:55 and 13:02 CET at lead-47's terminal: a module of its own, on a seat of its own, with an account of
its own, owns snapshotting, setting aside and restoring a module's kept data on the live bus, each act in the
hand-act log; the bus module keeps no restore permission (ADR 0626 §1 stays as it is), the snapshot account stays
hand-act log; the bus module keeps no restore permission but a protected snapshot's restore in place (§681, O-7), the snapshot account stays
limited to the nightly dump (ADR 0626 §2), and the backup holder only stores copies. ADR 0672 decides the account,
its grants and its callers; this line is written to it.
- **What it alone does.** It alone takes a restore point before a module moves across a `form` change, and alone
restores into the live bus. The bus module's snapshot before its own `recreate` or `drop` (ADR 0595 §5, ADR 0626
§4) and the nightly dump (ADR 0626 §2) stay theirs. The restore ADR 0667 §12 tests of the snapshot `drop` takes is
the recovery module's `recovery_restore` naming that snapshot, which it reads through the bus module's tool
`nats_snapshot_get` (class `read`, callers `holder:mesh-recovery@same-node` alone).
the recovery module's `recovery_restore` naming that snapshot, which it reads through the seat `mesh-broker`'s
verb `snapshot-get`, or has the keeper restore in place by `snapshot-restore` when it is protected (§681).
- **Its seat, module and node.** The seat `mesh-recovery`, held once for the mesh, is defined in mesh-contracts
beside `mesh-broker`, and the module `mesh-recovery`, in mesh-catalog, claims it; its glossary word is the
*recovery module*. It runs on the node of the holder of `mesh-broker`, read from the catalog half and the seat
@@ -63,41 +63,41 @@ are written into the lines of G-12 they name.*
nothing more; the point comes on `ended`, the restore on `restored`. A caller told `busy` asks again when it hears
the ending event of the act it was named (§560). The module writes each accepted act in its directory before it answers; at its start an act
accepted and not ended is ended `failed` with `stopped`, so its caller asks again.
- **Its events.** On `mesh.seat.mesh-recovery.event.` and kept last per subject, 90 days (§396): `ended`,
- **Its events.** On `mesh.seat.mesh-recovery.event.` and kept 90 days (§396): `ended`,
`restore-started`, `restored`, `set-aside`, `aside-discarded` and `point-removed`, each with the parts
`<module>.<kept>`, and `expiry-ended`, their fields as §560 gives them. The node agent wakes only on the ending
`<module>.<kept>` and kept last per subject, and `expiry-ended`, which has no parts and is not (§691), their fields as §560 gives them. The node agent wakes only on the ending
event of its own act, and its wait for `ended` or `restored` is bounded at 90 minutes from its first ask (§405,
§560), past which its have says the wait and the watchman's `delivery-waits` raises it.
- **Who calls it.** `snapshot-kept` and `restore-kept` are called by a node-engine holder (`seat:node-engine`): the
recovery module takes a call only from an account the bus proves to be a node-engine's, and the identity of who
asked is the check (the operator, 13:06 CET), every act logged; no want is read for it, since the operator,
- **Who calls it.** `snapshot-kept` and `restore-kept` are of class `act` and take its default (§610, the operator's
answer of about 16:10 CET: no verb keeps a named list): the bus proves who called, every act is logged with it,
and the identity of who asked is the check (the operator, 13:06 CET); no want is read for it, since the operator,
2026-11-11 13:10 CET: "if something asks for a restore, you should do it; he'll have his reasons".
`kept-points` takes the default of a read (§391). The operator, 2026-11-11 13:06 CET at lead-47's terminal: setting
aside is a move, not a removal, so deleting the live objects once their aside is whole and checked (§516) needs
no person's warrant, the data staying in the aside until the operator discards it; setting aside without restoring is
the operator's hand tool `recovery_aside`, and discarding an aside the hand tool `recovery_discard_aside`, both of
class `act`, callers `operator` and `sessions`, written in the hand-act log first; `restore-kept` refuses
`aside-only` and `discard-aside`, naming them (§516). The hand tools are checked by the identity of who asked: they are taken only from the operator's account as the bus proves it, and from sessions
acting on it as for every `act` tool (§391), with no warrant asked per act. Each act is written in the hand-act log of the seat `mesh-hand-acts` first, with the caller the
class `act`, taking its default (§610), written in the hand-act log first, each a verb of the seat (§656); `restore-kept` refuses
`aside-only` and `discard-aside`, naming them (§516). The hand tools are checked by the identity of who asked: each takes its class's default (§610), the bus proves the
calling account and the verb logs it with the `asker` the tool runner proved (§682), with no warrant asked per act. Each act is written in the hand-act log of the seat `mesh-hand-acts` first, with the caller the
bus proved and `why`; an act whose log write fails does nothing and answers `error`, naming the log (as §480).
- **The operator's hand tools.** `recovery_take` (take a point of one module and kept data now), `recovery_restore`
(restore one point) and `recovery_retry` (ask again the last failed act of a module and kept data), each of class
`act`, callers `operator` and `sessions` (§391), written in the hand-act log; they are checked by identity, as above.
`act`, taking its default (§610), written in the hand-act log, each a verb of the seat (§656); they are checked by identity, as above.
§566 adds `recovery_restore_aside` and `recovery_cancel`.
- **Its account.** The operator, 2026-11-11 about 14:15 CET at lead-47's terminal (the knowledge base's review of
ADR 0672, R3): the recovery module is one process with a bus account of its own (ADR 0436 §1, as ADR 0626 §1 has
the bus module's keeper), which holds the seat `mesh-recovery` and answers its verbs itself; each verb is one its
caller names (`by-caller`, ADR 0259 §3), so the bus proves who calls: a node-engine's account for `snapshot-kept`
and `restore-kept`, and the tool runner's account, which the sessions and the operator reach the bus as, for its
hand acts. Its tools bundle, carried by the tool runner, calls those verbs and holds no grant of its own. ADR 0672
caller names (`by-caller`, ADR 0259 §3), so the bus proves who calls, each taking its class's default (§610): a
node-engine's account for `snapshot-kept` and `restore-kept`, and the tool runner's account, which the sessions and
the operator reach the bus as, for its hand acts, are the callers it serves in practice. Its tools bundle, carried by the tool runner, calls those verbs and holds no grant of its own. ADR 0672
§4 records that this changes ADR 0436 §3 and §5 for this process alone. The account is composed by the grants part
from the seat's definition and the process's `bus` declaration: the bus's stream
snapshot and restore subjects with their chunk subjects, a stream's deletion and a durable consumer's creation,
only for the bus names of a module's kept data; the subjects its seat says and its hand-act log write; its
consumers of `node-backup`'s `item-started` and `item-ended`, filtered to those two events, each with the ordered
consumer §556 needs, from the process's `consumes`; its calls of `node-backup`'s `restore` as the caller `holder:mesh-recovery@same-node`, which
`node-backup`'s definition names for `restore` and which the backup holder takes only for the recovery module's
own item `points` (the operator, 13:02 CET); and nothing else. The bus names granted are those of every kept data
consumer §556 needs, from the process's `consumes`; its calls of `node-backup`'s `restore`, which takes its class's default with no own-item limit (§610, §555), and
of `mesh-broker`'s `snapshot-get` and `snapshot-restore` with `snapshot-get`'s chunk subjects, and the hearing
of `snapshot-restored` from its `consumes` (§681); and nothing else. The bus names granted are those of every kept data
of every catalog graph entry the catalog half keeps (§443) and of every entry a restore point names (an old
section's name before a form change, a template's binding after), read by the grants part through
`kept-points` (§565); no stream of the mesh's own (`CONTROL`, `NODES`, `EVENTS`, `DEAD_LETTER_NOTICES`, `DEAD_LETTERS`,
@@ -119,7 +119,7 @@ are written into the lines of G-12 they name.*
holder `backup-waits`, each naming the item or the act it waits on, and `kept-points` lists the act that runs
with since when and what it waits on. The recovery module consumes the item events that G's backup holder adds,
an edge of kind 6 from that holder to it, so a backup holder of before G, which says none, is never relied on.
- **Its health.** Its declared health check calls `kept-points`, and fails only while an aside's record ends at
- **Its health.** Its declared health check names the tool `recovery_kept_points` (§657), and fails only while an aside's record ends at
`removed` (§516) and no running act holds it, so a restore under way never fails it; it then also writes the
finding `aside-unrestored` (§561), which `recovery_restore_aside` (§566) clears.
- **Its order.** The node-engine's `invokes` of its verbs gives the edge of kind 6 from the recovery module to the
@@ -133,7 +133,7 @@ are written into the lines of G-12 they name.*
design's (review rounds 1 and 2; ADR 0672). How it is checked: test 36 with the real backup holder and the recovery
module; unit (grants part), the recovery module's account holds exactly the grants above, a call outside its names
refused, both a point's old-section names and a template's binding granted, and the bus module's and the snapshot
account's hold no restore; whole (recovery module), a call from an account that is no node-engine's refused, a
account's hold no restore; whole (recovery module), a hand act's log naming the account the bus proved and the asker, a
`busy` answer then the act on the other's `ended`, a second form change not woken by the first's `ended`, a dump
asked during a restore starting after `restored`, a restart during the dump starting no restore, an act whose
hand-act log write fails doing nothing, an unrestored aside failing the health check, a node-engine's `restore-kept` with `aside-only` or `discard-aside`
@@ -143,13 +143,13 @@ are written into the lines of G-12 they name.*
the recovery module's data item as any item, and the restore points and asides are listed by `kept-points` (§517),
an aside discarded by the hand tool `recovery_discard_aside` (§554); §303 and test 36 name the recovery module where they named the backup
holder and the bus module; §337's and §405's callers `holder:node-backup@same-node` go with the two verbs of
`mesh-broker`, which gains none; the seat `node-backup` gains nothing for kept data on the bus: its `aside` and
`mesh-broker`, which gains only §681's `snapshot-get` and `snapshot-restore`; the seat `node-backup` gains nothing for kept data on the bus: its `aside` and
`discard-aside`, the arguments `kept`, `form` and `fingerprint` of its `now` and `restore`, and its events
`ended.<node>.<module>.<kept>` and `restored.<node>.<module>.<kept>` go from its definition in mesh-contracts, and
it gains only `item-started` and `item-ended` with their `<item>` token, and names `holder:mesh-recovery@same-node`
among `restore`'s callers for the recovery module's own item (§554); §450's `aside` answer is `recovery_aside`'s;
it gains only `item-started` and `item-ended` with their `<item>` token, its `restore` taking its class's default
with no own-item limit (§610); §450's `aside` answer is `recovery_aside`'s;
§406's catch-up is recorded in the point as before. Why: one owner of the acts (§554). How it is checked: unit
(definitions), `node-backup` and `mesh-broker` hold none of the moved verbs; test 36.
(definitions), `node-backup` and `mesh-broker` hold none of the moved verbs, `mesh-broker` only §681's two; test 36.
## The review of §516 to §553, round 1
@@ -5,8 +5,10 @@ design: 47
milestone: G
updated: 2026-11-11
decisions:
- "ADR 0673"
- "ADR 0672"
- "ADR 0668"
- "ADR 0618"
- "ADR 0667"
- "ADR 0666"
- "ADR 0664"
@@ -215,7 +217,7 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
`kept-points` unanswered keeps a point's names it granted before, and a point's old-section names are added after
an `ended` and taken away after its `point-removed`.
- §566 **Two more hand tools: put an aside back, and cancel a waiting act.** (§554, §516) Each is of class `act`,
with callers `operator` and `sessions`, checked by the identity of who asked (§554), written first in the hand-act
taking its default (§610), checked by the identity of who asked (§554), written first in the hand-act
log of the seat `mesh-hand-acts` with the caller the bus proved and `why`, and an act of its own that runs one at a
time (ADR 0672):
- `recovery_restore_aside` (`module`, `kept`, `id`, `why`) restores an aside under the live names it was taken from,
@@ -247,7 +249,7 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
## The builder
- §568 **A transport fault in the builder's own read ends no build key, and a key's error says which kind of fault it
is.** (ADR 0664 §1 to §3, ADR 0668 §1 to §3; issue 1612, met again 2026-11-11)
is.** (ADR 0664 §1 to §3, ADR 0668 §1 to §3, ADR 0673 §1 to §4; issue 1612, met again 2026-11-11)
- **The builder's own reads.** The builder's reads of the build seats' state `steps` (its own step keys, §467,
§481) are kept calls (ADR 0668 §1, §2). A read that meets a transport fault, of ADR 0664 §1's classes or the
read's own deadline, changes nothing the builder holds and writes no build key. The builder's watch of `steps`
@@ -261,12 +263,13 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
build's own words) and never opens `transport fault:`. The builder's read tools show the error whole, so a reader
tells a passing cause from a real one without the journal.
- **What builds such a key again.** The operator, 2026-11-11 about 14:15 CET at lead-47's terminal, decided by the
record of the builder's retry (number to come), which changes ADR 0664 §3: a key whose error opens `transport
record of the builder's retry, ADR 0673 §1 to §3, which changes ADR 0664 §3: a key whose error opens `transport
fault:` is built again, once, as a new revision of the key (ADR 0429 §18), on the first event after its failure
that says its cause passed. Two events say so: the node-engine seat's `running.<node>.<module>` saying `running`
for a module whose catalog graph entry claims the build seat the failed step ran on (§546), on any node; and the
node-engine's `manifest-reads-changed` of the node the failed step ran on (ADR 0668 §4), which it says when that
node is heard again. The builder hears both through its consumers of the node-engine seat, read as §556 has it.
that says its cause passed. Two events say so (ADR 0673 §2): the node-engine seat's `running.<node>.<module>`
saying `running` for a module whose catalog graph entry claims the build seat the failed step ran on (§546), on
any node; and the node-engine seat's `manifest-reads-changed` of the node the failed step ran on with `why`
`returned` (ADR 0618 §4, ADR 0668 §4; §590), which it says when that node is heard again after being away. One
saying `why` `started` builds nothing again. The builder hears both through its consumers of the node-engine seat, read as §556 has it.
It builds a key again at most once per event heard, and only while the key's newest revision is that failure, so
one event starts one revision. A revision that fails again on a transport fault is said as every failed key is,
its error opening `transport fault:` again, and waits for the next such event. The builder's manifest `consumes`
@@ -280,18 +283,16 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
*Leave it*, a decline or the ask's expiry leaves the key failed until `builder_build_again` or a new
fingerprint. The failure stays said throughout. A key failed for any other cause
is never built again on these events. `builder_build_again` (ADR 0668 §5) and the builder's next start build it
again too, as before. The builder's read tool of a key names the event that built its newest revision again,
`event-builds`, and the open ask.
- **Before develop.** These lines rest on the record of the builder's retry, which changes ADR 0664 §3 (in force);
it is accepted before G's builder is built from them, and its number replaces "number to come" here.
again too, as before. The builder's read tool of a key, `builder_build` (§589), names the event that built its
newest revision again, `event-builds`, and the open ask.
Why: a read the builder made met `no responders` and failed a key, which then stayed failed for about 45 minutes
(issue 1612, 2026-11-11), against ADR 0664 §3 (a key is not written failed while its step runs on); the retry on an
event is the operator's answer. How it is checked: unit (builder), a `no responders` read of `steps` leaves the key
`building` and the next revision ends it; a third-run transport fault's key error opens `transport fault: no
responders`, and a compile error's does not; a `running` of a holder of that build seat builds the key again once,
and a second `running` while the new revision builds starts nothing; a node's `manifest-reads-changed` builds again
a key failed on that node; a key failed on a compile error is not built again on either event; after three failed
and a second `running` while the new revision builds starts nothing; a node's `manifest-reads-changed` with `why`
`returned` builds again a key failed on that node, and one with `why` `started` does not; a key failed on a compile error is not built again on either event; after three failed
event-started revisions the fourth event asks the operator once and builds nothing, and *Build once more* builds
one.
@@ -336,13 +337,13 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
saying the operator had not answered).
- **The router says the refusal.** The router (the holder of `operator-channel`) says every ask it refuses at
take-in, for each cause ADR 0259 §4 gives as it holds, on that asker's
`mesh.seat.operator-channel.event.decided.<asker>`. The event carries `outcome` `refused`, the ask's `id`, `why`
in the router's words and `at`, as an ask's end without a warrant is said (ADR 0259 §3).
- **Every asker ends its request on it.** mesh-sdk's asker client gives its caller `refused` with `why` as soon as
`mesh.seat.operator-channel.event.decided.<asker>`. The event carries `outcome` `refused`, the ask's id (the warrant's `ask`), why
in the router's words (its `words`) and `at`, as an ask's end without a warrant is said (ADR 0259 §3).
- **Every asker ends its request on it.** The asker client, mesh-contracts' `operatorchannelclient` (§638), gives its caller `refused` with `why` as soon as
it hears it. An asker's tool or act that waits on that ask answers `refused` with why, never "not answered yet";
the bus module's `nats_recreate` and `nats_drop` are among them.
- **The lifetime is held where every ask is made.** mesh-sdk's asker client changes in G's mesh-sdk release, which
every asker is built against (§47). mesh-sdk's asker client, through which every asker publishes
- **The lifetime is held where every ask is made.** `operatorchannelclient` changes in G's mesh-contracts release, which
every asker is built against (§47). That client, through which every asker publishes
an ask, refuses before it publishes an ask whose expiry passes the bound of its highest option level (`approve`
24 hours, `destroy` 10 minutes, ADR 0259 §4), naming the level and the bound. The bus module's keeper asks for
its level's bound in place of its fixed 12 hours (`AskLasts`).
@@ -356,7 +357,7 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
`asks` lists it with that outcome and words, as an ask that ended.
Why: ADR 0259 §4 refuses such an ask on the decided subject and bounds a destroy option at 10 minutes, and neither
reached the asker; the list is the operator's answer. How it is checked: unit (mesh-sdk's asker client), a table
reached the asker; the list is the operator's answer. How it is checked: unit (`operatorchannelclient`), a table
over the levels in which an expiry at the bound is published and one past it is refused before it is published,
and a `refused` that ends the request with why; the code review refuses an ask built outside the client; whole
(router, bus module), a refused ask is said on `decided`, a refusal of an id that is open leaves the open record and its tickets as they
@@ -514,7 +515,7 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
- **Its tools.** `indexer_pins` reads each kept revision with the records that pin it, whether it is current and
why, and the store's fill against its cap. `indexer_consumer` reads each of the indexer's durable consumers: its
stream, name, acknowledged sequence, the messages pending, and the last event applied with its `at`. Both are
of class `read`. `indexer_unpin` (`revision`, `record`, `why`; class `act`, callers `operator` and `sessions`,
of class `read`. `indexer_unpin` (`revision`, `record`, `why`; class `act`, taking its default (§610),
written in the hand-act log first) removes one record's id from a revision's list, for a `freed` that was lost,
and the removal rule then applies.
@@ -545,7 +546,7 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
each `seen` key's value, the time of the `step-ended` that wrote it. On each `step-ended` it handles, and at its
start, it deletes the `seen` keys whose `at` is more than 30 days old, at most 100 for each event, the oldest first,
so the bus keeps no key time-to-live and no template option is added; its tool `git_seen_expire` (class `act`,
callers `operator` and `sessions`, written in the hand-act log) runs the same removal now. The forge module being a foundation part
taking its default (§610), written in the hand-act log) runs the same removal now. The forge module being a foundation part
(§584), its state `failed-steps` is in its old `state` section, and a bundle reaches it through the tool runner's
state call by its name, as every old section is reached. Why: §487's 30 days needed
a trigger, and an event the module already handles is one (develop question of the forge module's developer). How
@@ -582,7 +583,8 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
it copied of each; a key already in the old section stays. This needs no other part's answer and adds no edge,
so it can close no cycle; holding its delivery until the template state is empty could wait for ever, since the
running registry keeps writing it. The template entries, no longer declared, then become orphans that `orphans`
lists, removed only by the operator's `drop` (ADR 0595 §4, §5), as §529 has it for the healers' old stream.
lists, removed only by the operator's `drop` (ADR 0595 §4, §5), as §529 has it for the healers' old stream. Their
other template entries become old sections, empty, nothing copied (§612).
- **The delivery order.** The running image registry, of its build before G, serves `mirror` only once its bindings
are composed, and both the bus module's new build and the image registry's own new build ask it for their base
images. So the grants part with design 25 *bindings-first-view* §9 is delivered first, alone, and composes the
@@ -616,9 +618,8 @@ form only. A note that corrects a line of G-12 or G-14 is written in that line;
(1), `fingerprints` every build-input fingerprint the runs named for that commit and module, and `at` as §581
has it. Both key forms of `failed-steps` are the forge module's own, defined in its own package, since no other
part reads that state.
- **The fill** of `failed-steps` that its read tool shows is counted by the forge module from the keys and value
bytes it reads, against the state's cap of 64 MiB, and said as counted, since a bundle's state call gives no
fill of the bus's own.
- **The fill** of `failed-steps` that its read tool shows is read through the bundle's state call
`mesh/state.info`, against the state's cap of 64 MiB (§623).
Why: the forge module's developer read these where no line spoke (develop, batch 5); each follows §39, §475, §487
and ADR 0666 §11. How it is checked: unit (forge module), a `required` failure recorded and an `optional` one not;
@@ -0,0 +1,216 @@
---
layer: to-be
status: in-progress
design: 47
milestone: G
updated: 2026-11-11
decisions:
- "ADR 0673"
- "ADR 0672"
- "ADR 0668"
- "ADR 0667"
- "ADR 0666"
- "ADR 0664"
- "ADR 0656"
- "ADR 0618"
- "ADR 0615"
- "ADR 0614"
- "ADR 0597"
- "ADR 0596"
- "ADR 0595"
- "ADR 0441"
- "ADR 0429"
---
# 47, milestone G: develop's sixth batch
One file of milestone G's part of [design 47](../47-delivery-from-commit-to-delivered.md); the part opens at
[G.md](G.md). Its design lines are numbered across every file of the part and cited as "design 47 G §n".
*§587 to §594 answer develop's sixth batch (lead-47's input of 2026-11-11, met in the join and develop after §560 to
§586). Each is of internal form only, within what the records and the lines it cites decide; §588 carries the operator's
answer of 2026-11-11, between 15:14 and 15:42 CET. A line of another file that one of them corrects is corrected in that line (§169, §170,
§171, §177, §226, §404, §462, §513, §568, and the event table of G-4); here only what is new.*
## The ledger's upkeep calls
- §587 **`mesh/ledger.fill` answers the history's fill, and `mesh/ledger.remove` has a request and an answer of its
own.** (§408, §462, §488, §155; ADR 0597 §1, §8, §10, §13) Both calls are in mesh-sdk's `go/buscalls`, as the
constants `LedgerFill` (`mesh/ledger.fill`) and `LedgerRemove` (`mesh/ledger.remove`), under the capability word
`ledger-upkeep-calls` (§488), served by the tool runner for the calling module's own ledgers alone, as every call of
the package is.
- **The fill.** The request is `LedgerRequest`, `{ledger}`, the ledger's local name in `requires`. The answer is
`buscalls.Fill` of the ledger's history alone: its messages, its bytes and the ledger's `cap` in bytes. The
history holds every operation and each instance's snapshot subject (ADR 0597 §1, §8), and the ledger's cap is
its history's (design 25's template row for a ledger, its state and index buckets being states), so neither the
fullest of the parts nor their sum is answered. A full state bucket is said by `apply`'s `state_behind` (ADR 0597
§6), not by this call. The gate's `filling`, `full` and `room` (§181) and `gate_records` (§159) judge this answer
against the 768 MiB of §155.
- **The removal.** The request is `LedgerRemoveRequest`, `{ledger, instance}`. The tool runner removes the
instance's operations and its snapshot from the history (by the instance's subjects), its key in the state bucket
and its keys in every declared index, and answers `LedgerRemoved`, `{instance, operations, bytes}`: the number of
operations removed and the bytes the history held less after the removal. An instance the ledger does not hold
answers `operations` 0 and `bytes` 0, so a removal said twice changes nothing. `Applied` gains no field: a
removal is no operation of the history and has no author. Only the owner's bundle calls it, through its tool
runner (ADR 0597 §13 leaves any other caller to the design, and the design names none); the gate calls it for a
record past its retention (§154).
This amends §462's "as `buscalls.Fill`" and its removal, which named no shape. Why: one cap, the history's, is
the ledger's, and a removal is not an operation (develop questions on §462 and §408, join of the tool runner). How
it is checked: unit (`buscalls`), each request and answer read; unit (tool runner), a fill answers the history's
bytes against the cap with snapshots counted, a removal answers the operations it removed, and a removal of an
unknown instance answers 0; whole (gate), a record past its retention is removed and the next fill is lower.
## The builder's publishes and its reads
- §588 **A reserved version left unpublished is published again, by the builder on an event or by a person's tool.**
(§52, §369, §381, §590, §610, §633; ADR 0673 §2; *Tools are how a module is spoken to*) A publish sends the
artifacts the build stored, by their digests, to the holder of the registry seat of their kind, under the same
version, and builds nothing, so it takes no build capacity.
- **By itself, on an event.** The operator, 2026-11-11 between 15:14 and 15:42 CET at lead-47's terminal, decided by
ADR 0674 (the builder's publish again), which extends ADR 0673 to publishes: the builder
publishes again, by itself, every reserved version `version-unreleased` names (§369), on the node-engine seat's
`running` of a module whose catalog graph entry claims the registry seat of the missing artifacts' kind, on any
node, and on the node-engine seat's `manifest-reads-changed` with `why` `returned` of the node that holder runs on
(ADR 0673 §2, §590); one with `why` `started`, or with none, publishes nothing. No timer. The builder's manifest
`consumes` both events (it does for ADR 0673 already).
- **Each event once.** The builder acts on a message of those subjects at most once, by its stream sequence: it
keeps, in its old `state` section `acted`, one key per subject holding the last sequence it acted on, so the read
of §556 at its start acts on each subject's last message only when that is newer than the kept one, and a message
read again after a restart starts nothing. The keys are one per subject of those two events, at most one per
module and node, about 0.1 KiB each; the section is capped at 1 MiB.
- **One queue per holder, kept on the bus.** Each registry seat holder has one queue of versions to publish, sent
one at a time, each bounded by §369's 10 minutes; a version already queued or being published is not queued
again. The queues are kept in the builder's old `state` section `publish-queue`, one key per queued version,
`<holder module>.<module>.<version>` (each token escaped as a key's name is, §73), holding its artifacts' digests,
when it was queued and whether its publish has begun; a key is deleted when its outcome is said. At its start the
builder reads the section whole and takes up every queued version again, a begun one sent anew (a publish of the
same digests under the same version, which the holder takes as it takes §52's), so a restart loses none. The
section is capped at 4 MiB (about 0.5 KiB a key); a version that would pass it is not queued, said in
`version-unreleased` as `queue full`, and waits for the next event or the tool. One event queues at most
`republish-max-versions` versions (a setting, §633, 50 unless set), at most one per `MAJOR.MINOR` of each module
(§369's key), the newest keys first; the rest wait for the next event or the tool. `builder_publish_queue` (class
`read`) lists the queued versions per holder, in order, with when each was queued and whether it has begun, and
the `acted` keys with their sequences.
- **An artifact no store holds.** It is named once in `version-unreleased` as not stored, and is not tried again on
events: its build key is built again by `builder_build_again`, which reserves anew.
- **The failure still said.** A publish that fails again keeps `version-unreleased` with the holder's words and
waits for the next such event.
- **By a person's tool.** `builder_publish_again` (class `act`, §610, written in the hand-act log) takes `module`
and `version`, each optional, and `why`: with both, that reserved version's missing artifacts (§52); with `module`
alone, each reserved version of that module whose artifacts are not all published, at most one per
`MAJOR.MINOR`; with neither, every version `version-unreleased` names, one module after another, the newest key
first. It queues as the events do and answers at once with the versions it queued; each outcome is said as
`package-published`, or left in `version-unreleased` with the holder's words.
- **Before develop.** These lines rest on ADR 0674, accepted before G's builder is built from them.
Why: the operator's answer; §369 gave the tool one version, and a recovery of a registry holder away for hours
left many (issue 1966 note 206); the queue, the sequence, the cap and the unstored artifact are the design's
(review: health and events 5, running and testing 4 and 7). How it is checked: unit (builder), a version left
unreleased is published again on the holder's `running` and on its node's `returned`, not on `started` or no
`why`, once per message: a restart reading the same last message starts nothing; a version queued is not queued
twice; one event queues at most the setting's number; an unstored artifact is named once and not tried on the next
event; whole (builder, a fake registry holder away and back), every unreleased version published with no hand act,
one at a time; a restart with three versions queued publishes the three; a fourth past the cap is said `queue full`;
one call with neither argument publishes three modules' versions.
- §589 **`builder_build` answers a module's build keys whole.** (§348, §568, §575; *one-question-one-tool*) The
builder's tool `builder_build` (class `read`) takes `module` and, optional, `fingerprint`. With `fingerprint`, it
answers that key whole; with `module` alone, the module's build keys newest first, at most 20, each with its
`fingerprint` whole (the build-input fingerprint as the catalog graph entry carries it, never cut), `state`
(`queued`, `building`, `done` or `failed`), the key's revision and since when, the step and its node, the error
whole, `event-builds` and the open ask (§568), and `newest: <branch>` for each watched branch whose newest catalog
graph entry of the module names that fingerprint, read from the builder's own view of the catalog half (§348). A
module none of whose keys the newest entry names says so, naming the entry's fingerprint. Beside it,
`builder_builds_since` gains `module`, which narrows its answer to that module's builds, and `builder_stuck`
answers each key's fingerprint whole. Which node wants which fingerprint is the assigner's to answer, through the
seat `mesh-wants`, never the builder's (ADR 0596); the words of a want's hold (§575) may shorten a fingerprint for
reading, while every field named `fingerprint` in a tool's answer is whole. Why: no read answered a module's
wanted build whole in one call (lead-47, issue 1966). How it is checked: unit (builder), `builder_build` by module
answers its keys with whole fingerprints and marks the one the newest entry names; `builder_builds_since` with
`module` answers that module's builds alone.
## A node's capabilities, said once
- §590 **One event says a node's capabilities: `manifest-reads-changed`.** (§177, §226, §404; ADR 0618 §3, §4, ADR
0673 §2, ADR 0614 §4) The node-engine seat's event `manifest-reads-changed`, on
`mesh.seat.node-engine.event.manifest-reads-changed.<node>`, published only by that node's node-engine (ADR 0363
§2), is the one event of a node's stated capabilities. It carries, as the seat's definition already has it,
`node`, `manifest-reads` (every capability the engine states now, whole), `at`, and `why`: `started` when the
engine started or what it states changed, `returned` when its node is heard again after being away (ADR 0673 §2).
G adds no event `capabilities-changed`: §177, §226, §404 and G-4's event table name `manifest-reads-changed` in
its place.
- **The assigner** consumes it, asks the seat's `manifest-reads` of that node again (ADR 0618 §3, §4), and makes a
new assignment graph revision only when the capabilities answered differ from those its newest revision placed
on, so a `returned` that changes nothing makes no revision.
- **The builder** consumes it as ADR 0618 §4 and §568 have it, and builds a key again only on `returned`.
- **An older node-engine's event.** A `manifest-reads-changed` with no `why`, from a node-engine of before G, is
read as `started`, so it builds and publishes nothing again.
Why: two events of one fact, one decided (ADR 0618 §4) and one the design's (§226), are a second source of the
same fact, and ADR 0673 §2 builds on the decided one's `why` (lead-47, on ADR 0673). How it is checked: unit
(assigner), a `started` with new capabilities makes a revision and a `returned` with the same ones makes none;
the code review finds no `capabilities-changed` in any module or seat definition at G's heads.
## The bus's object stores
- §591 **Which cap governs which object store.** (§58, §477, §513, §547, §607) The bus module plans:
- **each `<state>-objects`** with 16 MiB, unless the state's `requires` names `objects-max-mib` (§547, which
replaced §607's 64 MiB, which replaced §513's 256 MiB);
- **each `<module>_sent-objects`** with 256 MiB (§477, kept by §607);
- **a foundation part's old `objects` section** with the `max-mib` it declares, `sent-objects` 256 (§477).
No store is planned with 1024 MiB; the bus module's earlier commit that did is changed to these. Every one of
them counts in §547's sum against the account's limit. Why: three lines in sequence named three caps for one
store (develop question NATS1, join of the bus module). How it is checked: unit (bus module's plan), a table of the
three kinds with their caps, and a state naming `objects-max-mib` planned at that size.
## The gate from G's delivery
- §592 **The gate's merge path from G's delivery, whole.** (§97, §150, §151, §156, §162, §164, §166, §167, §169,
§542; ADR 0666 §10, §11, ADR 0596) The gate makes no merge and hands none to the forge:
- **Who merges.** A session merges through the seat `git`'s `merge-pull` (ADR 0666 §11). The gate calls no
`merge-pull`, its manifest `invokes` none, and it says no `owed` act `merge`: `owed`'s acts are the delivery's
view, a comment and the note's line (§167). The merge as an owed act is not built.
- **How the gate learns a merge.** From the indexer's `take-in.taken`, one record per source revision of a
watched branch the indexer takes in, a group's members in one (§151). The gate does not consume the forge
bridge's `merged` and `pushed` messages on its stream `forge` (§509); those are the indexer's. It consumes from
that stream only `owed.ended` (§164), through a durable consumer its manifest's `consumes` declares and its
binding names, so the gate's code names no module: the forge bridge's module name leaves the gate's code.
- **A group.** Its members land one by one (*Groups*), its end the runner's `group-ended` (§431); §166's order of a
group's merges ends at G's delivery.
- **What was owed at the switch.** Each `owed` key the old bridge had not performed becomes an act (§169). Each
`merges` key it had not performed is said once as `approval-carried` (§542) and becomes no act. The gate writes
`merges` no more (§156) and reads it no more, and keeps it until §97's removal (§542).
- **A carried approval nobody uses is seen.** For each `approval-carried` it says, the gate writes the finding
`approval-carried-open` (scope the gate, id `<repository>.<pull>`, each escaped as a key's name is, §73), naming the
head the Approve named, and clears it when it hears the seat `git`'s `pull-merged` or `pull-closed` of that pull
request, which its manifest `consumes`.
- **The approval.** No Approve is asked per merge (§150); an `approval` step's answer is the runner's (ADR 0666
§10, §542).
This replaces the merge parts of §169 and §171, and §170 from G's delivery. Why: the join kept the old hand-off
(`merges`, the bridge's messages, the bridge named in code) beside the owed acts, and the lines were spread over
four places (join of the gate). How it is checked: unit (gate's manifest), no `invokes` of `merge-pull` and no
`consumes` of `merged` or `pushed`; the code review finds no module name of the forge bridge in the gate's code
and no act `merge`; whole (gate, bridge), a merge made through `merge-pull` opens one record from `take-in.taken`,
and a `merges` key at the switch is said `approval-carried` and performed by nobody, its `approval-carried-open`
raised and then cleared by that pull request's `pull-merged`.
- §593 **The gate hears neither the teller's answers nor the view's reach.** (§150, §431, §512) The gate's reads of
the teller's `answered` and the status view's `reached`, and its calls of `teller_answers` and `view_reach`, served
the Approve per merge, which goes at G's delivery (§150, §431): the gate's `asks` go with it. So the gate's
manifest declares neither event in `consumes` nor either tool in `invokes`, and its code calls neither; the commit
that added those calls is undone. `view_reach` stays the status view's own read tool, answered from the catalog
half (§196, test 61). Why: §431's "by their events" named the readers that stay, and the gate is not one of them
(join of the gate). How it is checked: unit (gate's manifest), neither event nor tool named.
## Readers of a module's manifest
- §594 **The readers take a module's manifest from its catalog graph entry.** (§196, §421, §443, §445; ADR 0656 §6,
ADR 0615 §1) The holders of `mesh-config`, `mesh-provisioner` and `mesh-self-check`, and the claude-code module,
read a module's manifest from the catalog graph entry that carries it whole (§443), taken through
`meshcatalogclient`'s view of the catalog half, which follows each watched branch's newest `catalog-revision` and
asks `revision` again on a gap (§196). None reads a manifest by digest from the indexer, its state `manifests` or
its tools: `manifests` is kept only for node-engines from before G (§445). Why: test 61's "no call of the indexer
by name" holds for every reader of which modules exist, and the entry carries the manifest (join of these readers,
RDR1 to RDR4). How it is checked: test 61, a row for each of the four readers; the code review finds no read of
`manifests` in them.
@@ -0,0 +1,598 @@
---
layer: to-be
status: in-progress
design: 47
milestone: G
updated: 2026-11-11
decisions:
- "ADR 0673"
- "ADR 0672"
- "ADR 0668"
- "ADR 0667"
- "ADR 0666"
- "ADR 0660"
- "ADR 0656"
- "ADR 0626"
- "ADR 0619"
- "ADR 0618"
- "ADR 0596"
- "ADR 0595"
- "ADR 0579"
- "ADR 0573"
- "ADR 0448"
- "ADR 0429"
- "ADR 0363"
- "ADR 0286"
- "ADR 0283"
- "ADR 0259"
---
# 47, milestone G: develop's seventh batch, the operator's answers of 15:14 to 15:42 CET, and the refreshed build maps
One file of milestone G's part of [design 47](../47-delivery-from-commit-to-delivered.md); the part opens at
[G.md](G.md). Its design lines are numbered across every file of the part and cited as "design 47 G §n".
*§610 to §676 answer lead-47's seventh batch of 2026-11-11: the operator's answers of 2026-11-11 between 15:14 and
15:42 CET at lead-47's terminal (§610 to §613, and §588 in G-16), the design questions of the six refreshed build
maps, and develop's D-1 to D-15. Each other line is of internal form only, within what the records and the lines it
cites decide. A line of another file that one of these corrects is corrected in that line (§15, §30, §391, §394,
§423, §462, §515, §528, §570, §584, §585, §588, design 34 every-event §11, §19, §35, and design 46 7-approval-mark §22); here only what is new. A question one line answers
for several build maps is answered once, and the maps' names for it are given in brackets.*
## The operator's answers
- §610 **No verb keeps a list of named callers: every verb takes its class's default.** (§391, §392, §394; ADR 0286;
the records this ends are named below) The operator, 2026-11-11 about 15:40 CET at lead-47's terminal: "the operator
and agents may do everything, we might make it more strict and start asking for additional checks for agent stuff,
modules can get as many permissions as they want"; and about 16:10 CET: "we can always lock it down later, but we
most definitely don't want stupid things like 'named lists'".
- **The defaults.** A verb's definition names its class, and its callers are its class's default, which the
seat-definition reader applies: `read` takes `invokers`, `persons` and `sessions`; `act`, `warrant` and `accept`
take `operator`, `sessions` and `invokers`. `invokers` is every module whose manifest names the verb in `invokes`
(§391). A module tool's callers are its class's default alike.
- **No list.** A seat definition and a manifest name no `callers`: the seat-definition reader and the manifest
validator refuse the field, naming the verb or tool, and every `callers` G's heads carry goes in G's change. So
`git`'s `tag` (§370), the build seats' `run` (§229), `node-backup`'s `now`, `restore`, `aside` and `discard-aside`
(§405, §555), `restore` taking any module's item (no own-item limit), `mesh-recovery`'s `snapshot-kept` and
`restore-kept` (§554, §659), `mesh-broker`'s `snapshot-get` and `snapshot-restore` (§681), the node-engine's eleven verbs
(§393) and its `first-views` and `first-view-again` (§422), `operator-channel`'s `ask-again` (§394), the indexer's `indexer_unpin` (§579) and every hand tool of G
take their class's default. §392's prefixed forms and `holder:<seat>@same-node` (§336) are no longer written in
a definition; a part that must tell callers apart reads the account the bus proved (`by-caller`, ADR 0259 §3).
- **What stays.** Who may publish an event or a step's result stays its owner's alone (ADR 0363 §2, ADR 0666
§21), and a verb's own judgement of its input stays (`restore-kept` refusing `aside-only`, §516; `merge-pull`
counting a rerun only by the operator or the healers, ADR 0666 §11, §585).
- **The approval mark stays** (ADR 0286), so a `warrant` verb, or any verb a manifest marks, runs only on the
operator's Approve of that one call, whoever calls (§677).
This amends §391, §392, §393 and §394, and every line naming `callers`. Why: the operator's answers. How it is
checked: unit (seat-definition reader, manifest validator), a definition or manifest naming `callers` refused, and
a table row per class giving its default; whole (backup holder, recovery module), a `restore` from a session and
from the recovery module for another module's item taken, and a `snapshot-kept` from a session taken.
- §611 **Five parts move only their findings to templates in G.** (§529, §577) The operator, 2026-11-11 between 15:14
and 15:42 CET at lead-47's terminal: in G only the state `findings` and the finding stream of the teller, the
status view, the watchman, enrolment and the forge bridge move to `requires` templates (§529). Their other old
sections stay as they are, keys and names untouched: the watchman's `presence` and `stuck`, the teller's `answers`,
`asked` and `ends`, the view's `answers` and `reach`, enrolment's `enrolments`, and the forge bridge's `heads`,
`inputs` and stream `forge`. The manifest validator accepts these parts' other old sections until a later line
moves them; §577's rule for the finding words stands. [mesh-delivery DQ-4.] Why: the operator's answer. How it is
checked: unit (manifest validator), each of the five manifests of G's heads accepted with its old sections and its
finding templates.
- §612 **The forge module's and the image registry's other template entries become old sections, empty.** (§584) The
operator, 2026-11-11 between 15:14 and 15:42 CET at lead-47's terminal: the forge module's `incoming` (objects,
2048 MiB), `transfers` and `idle`, and the image registry module's `incoming` (objects, 8192 MiB), `transfers`,
`idle`, `findings` and stream `events`, move to old sections under the same names, each with the cap it declares
today, and start empty: nothing is copied. They hold uploads in flight, which their callers ask again, and
findings, which are stated again at the start (§384). The template entries, no longer declared, become orphans that
`orphans` lists, removed only by the operator's `drop` (ADR 0595 §4, §5), as §584 has it for `holds`, `windows`
and `mirrors`, which alone are copied. [mesh-catalog Q-F2.] Why: the operator's answer. How it is checked: unit
(manifest validator), both manifests of G's heads hold no template entry; whole (image registry), a first start
copies only `holds`, `windows` and `mirrors`.
- §613 **The gate's kept data, state by state.** (§15, §156, §172, §431, §542, §584) The operator, 2026-11-11
between 15:14 and 15:42 CET at lead-47's terminal:
- `pulls` and `asks` go with nothing copied (§172, §431): G's gate declares neither, and they are orphans that
`orphans` lists, removed only by the operator's `drop` (ADR 0595 §4).
- `merges` and `source` stay in the old `state` section (§542, §15).
- `deliveries`, `groups`, `owed` and `conditions` move to `requires` entries of the template `state`, under the same
names. The old sections' keys stay, read no more, so a revert's gate of before G finds them, until the operator's
`drop`.
- **Copied at every start that finds the old section changed.** The gate of G keeps, in a template state of its
own `copies`, one key per state with the old section's last revision it copied and when. At each start, before it
serves anything, for each of the four whose old section's last revision is newer than that key, it copies every
key changed since: a key whose write in the old section is newer than its template key's replaces it, a key
deleted in the old section after its template key's write is deleted in the template, and an older one is left.
A deletion G's gate makes itself in an old section (below) moves that state's mark in `copies` to the
deletion's revision in the same step, so the next start copies only what another gate wrote after it and never
deletes G's ended records from the template.
It then writes the mark, and says in its journal and in `gate_records` how many keys it copied and deleted of
each. So forward, a revert (whose gate of before G writes the old sections), and forward again carry every change
made while reverted, and nothing older overwrites a newer write.
- **What G ended, a revert does not take up.** When G's gate ends a delivery, a group or an owed act, it deletes
that key in the old section, the ended record staying in its template; so a revert's gate of before G finds only
what G left open, and never acts again on what G ended.
[mesh-catalog Q-G1.] This corrects §15's "the gate's other kept data stay in ADR 0595's templates". Why: the
operator's answer; the copy at every start is the design's (review: security and upgrade 5, running and testing 2).
How it is checked: unit (manifest validator), the gate's manifest of G's heads declares `merges` and `source` old
and the five templates; whole (gate), forward, then a revert's write and deletion in the old sections, then
forward: the template holds the reverted gate's write, the key it deleted is gone, and a key written in the
template after the revert is kept; a delivery G ended is not in the old section the reverted gate reads; a delivery ended, then a restart of G's
gate: its ended record is still in the template.
## Reading the catalog half, every part alike
- §614 **How a part reads the catalog half per watched branch, once for every part.** (§196, §199, §443, §459, §479,
§507, §521, §530, §532, §556; ADR 0656 §6, ADR 0596) Every part that reads the catalog half (a bundle or a process)
reads it so, and no part builds its own:
- **The reader.** Through `meshcatalogclient`, the client of the seat `mesh-catalog` in mesh-delivery's contracts
(ADR 0573 §1), wired on the one `go/views` reader the part already has: the bundle reader for a bundle, the process
reader (`views.ProcessReader`) for a process. Each watched branch is a view of that reader named
`mesh-catalog.catalog-revisions.<branch>` (§479), routed by §521, and the part's pass reads the views through that
reader alone.
- **The branches.** `meshcatalogclient` follows the branches itself, as mesh-delivery's code shared by every
reader of the seat: the registrations' watched branches, read by `registrations` at the start and followed by
`registration-changed` (§532); before the first registrations, the branches the gate's `source` answers, read at
the start and followed by the gate's `source-moved` (§507), which it stops following at the first
`registration-changed`. The gate's `source` given no branch answers `{branches}`, the branch of each
`source.<branch>` key it keeps, and given a branch that branch's source revision. mesh-sdk's `go/views` adopts and
drops the views (§532) and holds no seat's verb. The forge module, in TypeScript, follows the branches the same
way through the TypeScript form of `meshcatalogclient`, beside the seat's generated TypeScript contract in
mesh-delivery's contracts, on mesh-sdk's TypeScript view follower (§196).
- **The manifest entries every such part declares.** In `consumes`: `mesh-catalog.catalog-revision`,
`mesh-catalog.registration-changed` and `mesh-delivery.source-moved`. In `invokes`:
`seat:mesh-catalog.revision` (with the field `none-yet`, §617), `seat:mesh-catalog.registrations`,
`{verb: seat:mesh-delivery.source, fields: [branches]}` in the long form (§558), and `seat:mesh-catalog.entry` for a part that reads an entry by its fingerprint.
- **An older build's entry.** A part that resolves an entry by fingerprint (§459's reader, the node-engine's want,
§421) keeps beside its views the entries by fingerprint it asked of `mesh-catalog.entry`. When it hears a
`running` or a `wanted` naming a fingerprint that no branch's newest revision lists and it does not hold, it asks
`entry` once, as a kept call (ADR 0668 §1), and keeps the answer; it removes one when no `running` or `wanted` it
holds names that fingerprint any more. The reader it passes to a pure function (§459) answers from the views and
those entries alone, and reads no bus. Each kept entry is `asked`, `answered` or `not-found`:
- **asked**: the reader answers it as pending, a state apart from none; the part's health says it waits on that
fingerprint, and the node-engine's have says the phase `held` with its detail opening `entry-asked:` (§421's
`entry-missing` is said only once `entry` answered not found);
- **answered**: the answer starts a new pass at once;
- **not-found**: the part writes the finding `entry-not-found` (scope the part, id the fingerprint), cleared when
nothing it holds names that fingerprint or a later ask answers it.
`<module>_first_views` lists these entries with their state and since when, and the part's tool
`<module>_entry_again` (`fingerprint`; class `act`) asks one again now; the node-engine's are its seat's verbs
(§683).
- **A branch at `none yet`.** A view whose `revision` answered `none-yet` (§530) is ready and empty: the reader's
answer for that branch lists no entry.
- **The first pass.** A part acts on nothing until every branch it follows has its first view, `none-yet`
counting as one; until then `<module>_first_views` says each branch `waiting` and its health says it waits (§200,
§556). After its first views it acts on what it holds, and a view behind is read again by §196.
- **The node-engine follows no branch.** It reads the catalog half only by the fingerprints its wants name
(§421), through the entries above, and declares none of the branch entries; it never waits on a branch's view, the
registrations or the gate's `source`, so it composes the gate (and every part) before any of them answers, and no
cycle runs through it at G's delivery. It keeps those entries on its node and serves their rows and ask again as
its seat's verbs (§683). The grants part composes a want's account before its first views (§684).
- **A dropped branch.** When `go/views` drops a branch's view (§532), it clears, through the part's mesh-sdk writer (§639: a views reader writes its findings through the part's writer, never a writer of its own),
every finding that view raised (`first-view-unanswered`, `view-waits`, `view-unsaid`, `view-too-large`), says the
drop and each cleared finding in the part's log, and removes the view's row of `<module>_first_views` [D-12].
This is the one form for every reader of which modules exist and of the catalog graph's entries: the tool runner
(its owner of the assigner's views, §616, and its branches, [mesh-tools Q-R12]), the grants part [D-5], the
readers of §594, the node-engine (§421) and the Go build module (§615); and it answers mesh-sdk's question on
§532. Why: the same question came from five build maps, each about to build its own form (develop's D-5). How it
is checked: unit (`meshcatalogclient`), a branch added by `registration-changed` and one by `source-moved` before
the first registrations are followed, an entry asked once on a `running` of an unlisted fingerprint and dropped
when nothing names it, a pending entry read apart from none and a not-found one raising `entry-not-found`, a
`none-yet` branch read as empty; unit (gate), `source` with no branch lists the branches; whole (grants part, tool
runner), a part started with one branch at `none-yet` acts once every first view is in; a dropped branch's findings
are cleared; whole (node-engine), a want composed with no branch view and no `source` answered.
- §615 **The tool runner's and the Go build module's manifests come from the entry too.** (§594, §614, §443, §445,
test 61) The mesh MCP server of the tool runner takes each module's manifest from its catalog graph entry, through §614's
reader; its first view of the indexer's `manifests` and its `consumes` of `mesh-delivery-indexer.views.manifests`
go [mesh-tools Q-R2]. The Go build module's compose ask takes each manifest whole from the entry its fingerprint
names, by `seat:mesh-catalog.entry` (§506's `manifest-digest` naming the entry's), and its `invokes` of
`mesh-delivery-indexer.indexer_read_manifests` goes [mesh-catalog Q-B1]. Both join §594's readers. Why: test 61, and
`manifests` is kept only for node-engines of before G (§445). How it is checked: test 61, a row each.
- §616 **The assigner's views are named by the seat, in the code and in the manifest.** (§428, §7) The tool runner's
code reads the module that holds `mesh-module-assigner` from the catalog half's claims of that seat (§614) and
names the views `<that module>.assignments` and `<that module>.seat-holders` from it, never by a name fixed in code.
Its manifest's `consumes` names them by the seat, marked so as `invokes` marks a seat:
`seat:mesh-module-assigner.views.assignments` and `seat:mesh-module-assigner.views.seat-holders`, which the grants
part resolves to the seat's holder from the catalog half as it resolves a seat's events, so a new holder is
granted with no change of the manifest. The seat `mesh-module-assigner`'s definition declares its states
`assignments` and `seat-holders` as views (ADR 0571 §6), and the manifest validator refuses a seat-named view the
definition does not declare, naming it. The edge is of kind 6 from the seat's holders to the consumer, held until a
holder runs a build whose seat definition declares the view, as §558 derives it for a field. mesh-sdk's manifest
reader reads the `seat:` form in `consumes`, first in §494's second reader release (§621), which the parts reading
manifests take before G's merge. Design 46
7-approval-mark §19's holder of `operator-channel` is read from that view so found. [mesh-tools Q-R3, Q-R17.] Why:
§428 removes the name from code, and the catalog half says who claims a seat (review: seams and tools' note). How
it is checked: unit (tool runner), a fixture catalog half naming another holder of the seat gives views of that
name; unit (grants part), a seat-named view granted for the seat's new holder; unit (manifest validator), a
seat-named view the definition lacks refused; the code review finds no assigner
name in the tool runner's code or manifest.
- §617 **The long form of an `invokes` entry stands beside `*`.** (§558, §545) A manifest's `invokes` may hold `*`
and long-form entries together: the tool runner's is `*`, then `{verb: "seat:mesh-catalog.revision", fields:
["none-yet"]}` and `seat:mesh-catalog.entry` with no fields named [mesh-tools Q-R4, Q-R18]. The manifest validator
accepts a long-form entry beside `*`, and each named verb gives its edge of kind 6 (§558). In mesh-sdk's Go form,
`Invokes` stays the list of verbs, each long-form entry's verb among them, and `InvokeFields`, beside it, maps a verb
to the fields it names, so none of `Invokes`' readers changes [mesh-sdk 4]. Why: `*` lets a part call any verb, and a
long-form entry is how it says it relies on a verb or a field, which `*` cannot say. How it is checked: unit
(manifest reader), `*` with two long-form entries read into `Invokes` and `InvokeFields`; unit (manifest
validator), the tool runner's manifest accepted and its two edges derived.
## The tool runner
- §618 **The tool runner's own fingerprint reaches no bundle.** (§205, §519; design 46 7-approval-mark §5, §19)
`MESH_FINGERPRINT` is one of the tool runner's own words, which `BundleEnv` hands no bundle: a bundle reads only the
fingerprint the node-engine composed into its environment (§519). A bundle whose environment carries none has no
served entry the tool runner can read, so every marked call on that runtime is refused, as design 46 7-approval-mark
§5 and §19 have it. [mesh-tools Q-R5, extended.] Why: a bundle given the tool runner's own value is judged against
the tool runner's entry. How it is checked: unit (tool runner), a bundle composed with none sees none and its marked
call is refused; one composed with a fingerprint sees its own.
- §619 **A foundation part's bundle reaches its old `objects` sections through its membership.** (§407, §518) The grants
part issues each old `objects` section of a foundation part in the membership, as it issues its old `state`
sections: mesh-sdk's membership gains `Objects`, beside `State`, mapping each section's name to its object store.
The tool runner resolves a put or get session's `name` (§518) of a foundation part's bundle in that field, and of any
other part in its state's objects store and its sent store, as §518 has them. [mesh-tools Q-R6.] Why: §407 binds
that bundle to its old `objects` section and no membership field carried it. How it is checked: unit (membership),
the field read; whole (tool runner), a foundation part's bundle puts and gets a value over the limit in its old
section.
- §620 **A sent object over the limit is read by a get session.** (§59, §518) The tool runner's `mesh/objects.sent-get`,
asked for another module's sent object larger than the bus's per-message limit, answers `{chunked: true, size}` and
sends the object through §518's get session on the bundle's channel, read from the sender's sent store under the
standing grant §59 gives the caller. [mesh-tools Q-R7.] Why: a bundle calling a verb whose reply is larger had no
way to read it. How it is checked: whole (tool runner), a reply's object of 20 MiB read whole by a bundle.
- §621 **What the second reader release reads, and how it is released.** (§494, §545, §605, §508, §554) §494's release
reads, beside `calls` (§605) and the long form (§617), the tool runner's artifact word `states` [mesh-tools Q-R8,
mesh-sdk 3] and `by-caller` on a verb, which the seat definition's format gains for `snapshot-kept` and
`restore-kept` (§554) [mesh-contracts DQ-1] and every verb G gives the form (§681, §682), and the `seat:` form
of a seat-named view in `consumes` (§616). Since it also carries §508's validator rule, which is the indexer's,
the release is a delivery group of mesh-sdk (the reader's words) and mesh-delivery (the rule), checked and delivered
together [mesh-sdk 5]. Why: the running reader refuses an unknown word, and the rule is mesh-delivery's code. How
it is checked: unit (the release's reader), `states` and a verb's `by-caller` read; the group's one check.
- §622 **Where the capability words and `mesh/capabilities` are named.** (§462, §488, §576) mesh-sdk names each
capability word as a constant beside the calls of its family: the bus-state words (`bus-state-calls`,
`ledger-calls`, `ledger-upkeep-calls`, `sent-objects-calls`) in `go/buscalls`, and `put-sessions`,
`finding-calls` and `seat-settle-delay` with `mesh/publish` in `go/stdio.go` and `src/stdio`; the call
`mesh/capabilities` and its answer `{words}` are named there too, since `go/buscalls` holds only bus-state calls.
Which calls each word covers stays the tool runner's (§462). mesh-sdk's Go package names all four words its calls
need in `calls` (§605), the npm package `finding-calls` alone, since its code makes no session, ledger or sent-object
call. [mesh-tools Q-R9; mesh-sdk 1, 2, 11.] This amends §462's "mesh-sdk gains no capability list": it gains the
words, not the families. Why: the bundle fails naming a word (§488), so it must know it. How it is checked: unit
(mesh-sdk), each call's word; unit (tool runner), its families use mesh-sdk's constants.
- §623 **The forge module's fill is read through `mesh/state.info`.** (§585) A bundle's state call `mesh/state.info`
answers a state's bucket against its caps, so the forge module's read tool shows `failed-steps`' fill from it, not
counted from the keys. §585's fill bullet is corrected so. [mesh-tools Q-R11.] Why: the call exists. How it is
checked: unit (forge module), the fill read from the call.
- §624 **The tool runner's manifest `version` is §368's.** (§368, §583) The tool runner's manifest names `0.1`, the
`MAJOR.MINOR` of §368, as every manifest; design 34 every-event §11's "Its `version` stays `2`" is corrected.
[mesh-tools Q-R13.] Why: §368 is the later line and holds for every manifest. How it is checked: as §368.
- §625 **The act's build of a marked wait is the tool runner's own `MESH_FINGERPRINT`.** (§205; design 34 every-event
§35) The build an act of a marked wait names is the tool runner's own fingerprint, read from its `MESH_FINGERPRINT` at
its start (§205), the fingerprint the node-engine's `running` says of it; the tool runner consumes nothing more for
it. Design 34 every-event §35 is corrected so. [mesh-tools Q-R14.] Why: the node-engine hands each process its own
build (§205). How it is checked: unit (tool runner), the act carries the fingerprint of its environment.
- §626 **A module ends G with no more tests than it starts with.** (§419, test budget) §419's count starts, for each
module, at the commit of its repository's main from which G's branch of that repository starts (its merge base with
that branch), and the check compares the module's count at G's heads against it. This holds for the tool runner's
work of design 34 every-event and design 46 7-approval-mark: the new tables and the whole test of design 46
7-approval-mark §22 and design 34 every-event §19 replace existing tests, and every test they delete counts. A test
moved with its code (§655) counts in the module it moves to, as no new test there, and as deleted in the module it
left. [mesh-tools Q-R16.] Why: §419 is milestone G's rule and both parts ship in G's release. How it is checked: the
check counts each module's tests against its count at that commit.
## The node-engine and the node agent
- §627 **The node-engine is a foundation part.** (§6, §529, §577; ADR 0619 §1 to §3) The node-engine composes every
part, the bus module included, before the bus module can answer, so it is a foundation part as ADR 0619 §1 defines
one, as §584 has it for the forge module and the image registry. The manifest validator's list gains the holder of
the seat `node-engine` in G's change; its kept data stays in its old sections, its old stream `mesh-host-findings`
among them, and it opens them by their declared names (§577's old-name path), not by `Binding`. [the node-engine's map, RQ-1.]
Why: ADR 0619 §1. How it is checked: unit (manifest validator), the node-engine's manifest of G's heads accepted with
its old stream.
- §628 **G removes the node-engine's reads of the indexer and the builder alone.** (§421, §195, §435) In G the
node-engine's reads of the indexer's and the builder's states go, as §421 names them. Its reads of the assigner's
`assignment` and `candidates`, the provisioner's `provisions` and the vault's `signers` and `node-logins` stay in G
(§459's input among them), and `compose-candidate` keeps its reads of `candidates`, `manifests` and `verdicts` until
it goes with the checker (§435). §421's "holds no read of another module's state" is checked for the indexer's and
the builder's states. [the node-engine's map, RQ-2, RQ-3.] Why: G moves the reads §421 names; the others need verbs no line of G
gives, and they are reads of another module's state that ADR 0596 §2 ends when their owners move. How it is checked:
the node-engine's account holds no read of the indexer's or the builder's states.
- §629 **The have's wait and the node-engine's `wait-held`.** (§423, §498, §499, §541, §560) A node-engine's
`wait-held` and `wait-ended` carry the fields of the seat `mesh-wants`' wait (§498, §512: `owner`, `step`, `node`,
`on`, `bound`, `since`, `words`) and `reason`; `node` is the node waited on (the build node for a kept build), and
the subject's `<node>` the node that says it [mesh-contracts DQ-3]. The have's value gains `wait`, one such wait with
`ask-at` and `act` (§560), present while the have waits; `waits`, the composition digest of the phase
`waiting-yes`, stays as it is [the node-engine's map, RQ-4, mesh-contracts DQ-4]. This corrects §423's "with no new field". Why:
§499 and §560 need the wait in the have. How it is checked: unit (node-engine seat's contract), `wait` read with
both fields; whole (node-engine), a kept-build wait says it.
- §630 **At a revert the node agent reads the point from `kept-points`.** (§516, §560) At a revert the node agent asks
`seat:mesh-recovery.kept-points` for the module and kept data, takes the point of the form and fingerprint it reverts
from, and passes its `snapshot` and its catch-up to `restore-kept`; its manifest names the verb in `invokes`.
[the node-engine's map, RQ-5.] Why: `ended` is kept last per subject, so a later point replaces it. How it is checked: unit (node
agent), a revert after two points restores the one of its form.
- §631 **A manifest's `catch-up` is a tool and its arguments.** (§451, §516) A manifest declares `catch-up` as `{tool,
arguments}`, mesh-sdk's reader takes it whole (a string of before G read as `{tool, arguments: {}}`), and the node
agent passes it to `snapshot-kept` unchanged. [the node-engine's map, RQ-6.] Why: `snapshot-kept` takes both. How it is checked:
unit (reader), both forms read.
- §632 **The node agent's `why`.** (§516) It gives `snapshot-kept` the `why` "form change of `<module>`'s `<kept>` to
`<fingerprint>`" and `restore-kept` "revert of `<module>`'s `<kept>` from `<fingerprint>`", which the hand-act log
keeps. [the node-engine's map, RQ-7.] How it is checked: unit (node agent), both words.
- §633 **§557's bounds are `config` keys.** (§557) Each bound §557 makes a setting is a key of the part's manifest
`config`, read at its start through mesh-sdk's `stdio.Config`, its default the design's value; the node-engine's are
`recovery-wait-minutes` (90) and `view-waits-minutes` (5) [the node-engine's map, RQ-8], the condition keeper's two hours of §540
`owner-handover-minutes` (120) [D-7], and every other part's is named `<what>-<unit>` alike. The gate's own settings
stay where they are. Why: one form for every part's bound. How it is checked: each whole test sets its bounds in
its fixture's `config`.
- §634 **The node-engine's own name, everywhere it is used, is §527's.** (§428, §527) Its discovery, its finding
stream's subject and observer, and the vault's login names read the node-engine's own module from §527, as its
guard, compose and bootstrap do. [the node-engine's map, RQ-9.] How it is checked: the code review finds no fixed name.
- §635 **The node-engine invokes `withheld`; it gains no `uses`.** (§555, §558; design 46 7-approval-mark §6) Its
manifest names `seat:mesh-grants.withheld` in `invokes`, which gives the served-verb edge, and gains no `uses`.
[the node-engine's map, RQ-10.] How it is checked: unit (manifest validator), the edge derived.
- §636 **The private network module's name is its own manifest's.** (§428) `overlay.Name` stays the `module` the private network package's (`overlay`)
computed manifest names itself; mesh-networking removes the fixed `mesh-provisioner` alone, read from the seat
list's holder of `mesh-provisioner`. [the node-engine's map, RQ-11.] How it is checked: the code review.
- §637 **Test 36 reads the builds of before G through the registry seat.** (§405, §555, §610) Test 36's run, in the
check the running checker makes of G (ADR 0667 §2), fetches the builds of before G and the recovery module by their
fingerprints through the seat `oci-artifact-registry`, read-only, under the checker's account, its manifest naming
the fetch in `invokes`, granted by §610's default. [the node-engine's map, RQ-12.] How it is checked: test 36 runs in that check.
## mesh-sdk's shared parts
- §638 **The asker client is mesh-contracts' `operatorchannelclient`.** (§570, §586; ADR 0573 §1) The client §570
names is `operatorchannelclient`, of the seat `operator-channel`'s contracts in mesh-contracts, which already
bounds the expiry; `Take` answers `Refused` with the router's words. mesh-sdk builds nothing for it. The fields
§570 names are the contract's: on `decided`, the warrant's `ask` (the ask's id) and `words` (the why); on the record,
`state` `refused` and `answer`, and the record gains `at`. §570 is corrected so. [mesh-sdk 6, mesh-contracts DQ-9,
DQ-8.] Why: a seat's client lives in its contracts package (ADR 0573 §1). How it is checked: unit
(`operatorchannelclient`), a refusal answered and an expiry past its level's bound refused.
- §639 **A views reader writes its findings through the part's writer.** (§461, §485, §580) `go/views`' findings
(`view-waits`, `first-view-unanswered`, `view-unsaid`, `view-too-large`) are written through the part's mesh-sdk
writer as kinds of their own (§580), and the writer's statement covers them beside `observe`'s. [mesh-sdk 7.] How it
is checked: unit (writer), a statement keeps the reader's open findings.
- §640 **The finding writer reads the generated types; it keeps no copy of the layout.** (§439, §452) mesh-sdk's
writer, in Go and TypeScript, defines the small interface it needs of a finding's body (its key's parts, its
`since`, its kind) and takes the types the seat generator emits for `findings` through it; it holds no
`FindingBody`, `FindingCleared`, `FindingsStated` or `FindingsAsked` of its own, so there is one layout and nothing
to keep equal. [mesh-sdk 8.] Why: a copy held equal by a test in another repository is not checked on a change of
mesh-sdk alone, and the TypeScript copy by none (review: running and testing 6). How it is checked: unit (writer,
both languages), a generated body written and stated; the code review finds no copy.
- §641 **A stream kept last per subject: where it is read and how it is told.** (§396, §556) mesh-sdk's
`HearSeatEvents` makes §556's read at the start with an ordered consumer, then follows the durable, and names the
stream `waiting` in `<module>_first_views` until the read ends [mesh-sdk 9]. The grants part tells such a stream by
the seat definition's `keep` of that event, and grants its ordered consumer from the reader's `consumes`
[mesh-delivery DQ-7]. `wait-held` and `wait-ended` of the node-engine each have their own stream (§396), with
`running`'s keep (no age limit, last per subject) [mesh-contracts DQ-2]; §556's "on the stream `running` is kept
on" reads so. How it is checked: unit (mesh-sdk), the start read then the durable; unit (grants part), the ordered
consumer granted from `keep`.
- §642 **A binding's `objects` is a state's field, and the sent store an entry.** (§399, §513) In mesh-sdk's
membership, `objects` is a field of a state's binding, and the sent store an entry of its own, local name
`sent-objects`, as §399 has it; §513's "fields of `membership.BusBinding`" reads so. [mesh-sdk 10.] How it is
checked: unit (membership).
- §643 **Put and get sessions are Go only in G.** (§518, §57) A TypeScript bundle's value over the per-message limit
fails at once, naming the limit, in G. [mesh-sdk 12.] How it is checked: unit (mesh-sdk TypeScript), the refusal.
- §644 **`Hearing` is told which events are defined.** (§474) `catalogue.Hearing` takes, beside the manifests, a
function the grants part passes, answering whether an event is defined by a seat definition of the seat list or is
one of mesh-sdk's finding events. [mesh-sdk 13.] How it is checked: unit (`Hearing`).
## mesh-delivery's parts
- §645 **The checker's seat definition is in mesh-delivery's contracts.** (§528) §528's "the checker's seat
definition in mesh-contracts" is corrected to mesh-delivery's contracts module, where `tag-wanted` is removed.
[mesh-delivery DQ-1, mesh-contracts DQ-13.]
- §646 **The runner's bodies name `fingerprint`.** (§585) The runner's `step-ended`, pipeline and wait bodies name the
build-input fingerprint `fingerprint`, not `fp`, as §585 reads it. [mesh-delivery DQ-2.] How it is checked: unit
(runner's contract).
- §647 **The runner times an ask from `ask-taken`.** (§535, §602) §535's five minutes run from the ask until the build
seat holder's `ask-taken` (§602), which the runner consumes; `step-started` is the runner's own. [mesh-delivery
DQ-3.] How it is checked: whole (runner), an ask no holder takes ends its pipeline after the setting.
- §648 **A `take-in.taken` entry carries the pull request.** (§476, §542) An entry is `{module, commit, fingerprint,
repository, pull}`, the last two from a `merged` message and absent for a `pushed` one. [mesh-delivery DQ-5.] How it
is checked: unit (indexer's contract).
- §649 **`ParseStepKey` answers the asker.** (§484) `buildstep.ParseStepKey(key)` answers `(asker, node, id, err)`,
`asker` empty for main's form. [mesh-delivery DQ-8.] How it is checked: unit, both forms.
- §650 **The `mesh-builds` client's name.** (§586) It is `contracts/go/clients/meshbuildsclient`, with `Reserved`.
[mesh-delivery DQ-9.]
- §651 **The checker's first tier of the change's own acting parts is not built.** (§528, §66, §279 to §290) The
checker of G's heads never makes G's own check (ADR 0667 §2), so the first tier of §279 to §290 (CK6 to CK8) is not
built in the checker; the runner's steps check a change's acting parts from G's delivery. [mesh-delivery DQ-10.]
- §652 **`pins-stated` is kept as `pinned` is.** (§579) The seat `mesh-delivery`'s definition gives `pins-stated` the
`keep` of `pinned`, so the indexer's consumer from the stream's first message hears it. [mesh-delivery DQ-11.]
- §653 **The teller's reads of the gate's `asks` go.** (§431, §512, §593) The teller's manifest drops its `reads` of
`mesh-delivery-gate.asks`, since the Approve per merge goes. [mesh-delivery DQ-12.]
## mesh-catalog's parts
- §654 **The readers of the forge module's old events and tools move to the seat `git`.** (§310, §311, §313, §431;
ADR 0659 §1) [mesh-catalog Q-F1.]
- **mesh-config** consumes `git`'s `pull-merged` and `pull-closed` in place of `gitea.pull.merged`,
`gitea.pull.closed` and `gitea.forge.pull-request.closed`, and invokes `seat:git.pull` in place of
`gitea.gitea_get_pull_request`; **the records module** consumes `git.pull-merged`.
- **The order.** Main's forge module says its own `pull.merged`, not the seat's events, so each consumer of
`git.pull-merged` or `pull-closed` is ordered after the forge module's version that says them, by the edge of kind
6 its `consumes` gives (§558); its durable consumer is made from the stream's first message, so no merge said
before it began is missed, and no catch-up through `git.pull` is needed.
- **mesh-issues** invokes `seat:git.pull` and `seat:git.contains`, and reads a pull request's steps from the
runner, `seat:mesh-pipeline-runner.list` and `seat:mesh-pipeline-runner.show` in its `invokes`: `list` given the
pull request's head commit answers that commit's pipelines, one per module of the repository it builds (G-6),
and `show` of each answers it whole with each step's result, the review step's among them (ADR 0666 §22). For a
repository of several modules mesh-issues shows one row per module's pipeline, each with its steps; a commit
`list` answers with no pipeline is shown as "no run". The forge's own
reviews are read nowhere: G keeps no review record (ADR 0659 §1). Its read of `seat:mesh-delivery.checks` and
`gitea_pull_reviews` go.
How it is checked: unit (manifest validator), each manifest of G's heads names none of the old events or tools;
unit (mesh-config, records), each acts on a `git.pull-merged` fixture; unit (mesh-issues), a commit with no
pipeline shown as "no run", and one of a repository of two modules shown as two rows.
- §655 **The take and restore of one stream is shared code in mesh-sdk.** (§554, §464; ADR 0672 §1) The code that
takes and restores one stream's snapshot moves whole from the bus module's `bussnap` into mesh-sdk's `go/bussnap`,
imported by the bus module (its snapshot before `recreate` and `drop`), by the recovery module (its points and
restores), and by any part that takes the nightly dump's stream snapshots; no module imports another module's
package, and no third copy is kept. The edge of kind 1 from mesh-sdk's package (§47) orders its publication before
the bus module's and the recovery module's builds. [mesh-catalog Q-R1.] Why: code two modules share lives in
mesh-sdk (§464's reason). How it is checked: the code review finds one copy.
- §656 **The hand tools' acts are verbs of the seat `mesh-recovery`.** (§554, §566; ADR 0672 §7) Each hand tool's act
is a verb of the seat `mesh-recovery`, `by-caller`, answered by the process: `take`, `restore`, `retry`, `aside`,
`discard-aside`, `restore-aside`, `cancel` and `expire-points`, each of class `act` (§610), beside `snapshot-kept`,
`restore-kept` and `kept-points`, all defined in mesh-contracts. The tools bundle's `recovery_*` tools call them,
named in its `invokes`, and hold no grant of their own; the bus proves the tool runner's account, as ADR 0672 §7
has the bundle call the seat's verbs. [mesh-catalog Q-R2.] Why: ADR 0672 §7 (review: seams and tools 2). How it is
checked: unit (mesh-recovery's contract), each verb read; whole (recovery module), each hand tool acts through its
verb. Each verb's fields and answers, and the `asker` each hand verb takes and logs beside the account the bus
proved, are §682's.
- §657 **The recovery module's health check is a tool.** (§554, §468, §561) The recovery module serves the read tool
`recovery_kept_points`, answering what `kept-points` answers, its bundle naming `seat:mesh-recovery.kept-points` in
`invokes`, and its declared health check names it. The check fails while an aside's record ends at `removed` and no
running act holds it, as §554 has it, which `recovery_kept_points` answers. §554's "calls `kept-points`" is
corrected so. [mesh-catalog Q-R3.] How it is checked: unit (manifest validator), the check names a served tool;
whole (recovery module), an unrestored aside fails the check.
- §658 **A module new in G starts with the tests its lines name.** (§419) A module new in G counts from none: its
tests are the checks its design lines name (for the recovery module, its one whole test and the unit rows of its
lines), within the per-module budget. [mesh-catalog Q-R4.]
## mesh-contracts' seat contracts
- §659 **`snapshot-kept` and `restore-kept` are of class `act`.** (§554, §610) Class `act`, taking its default, as
every verb does (§610). [mesh-contracts DQ-5.]
- §660 **A finding's key and value.** (§461, §437, §439) The state `findings` keeps the key `<scope>.<id>.<kind>` and the
value `#/bodies/Finding`. [mesh-contracts DQ-7.]
- §661 **`list` shows the caps.** (§547) The seat `mesh-broker`'s `list` answer gains `caps {sum-mib,
account-limit-mib, server-limit-mib}`, an absent limit meaning unlimited. [mesh-contracts DQ-10.]
- §662 **A subject function with parts can fail.** (§523) A generated subject function with `<part>` tokens answers
`(string, error)` in Go and throws in TypeScript for an empty part, `.` or `..`. [mesh-contracts DQ-11.]
- §663 **The condition keeper's sources are the bus module's `created` events, and it follows a moved stream.** (§515,
§529, §611, §612) The keeper takes a source when it hears the seat `mesh-broker`'s `created` of a part's
`finding-events` entry, named in its `consumes`, and a foundation part's old finding stream from the catalog half
(§614); at its start it reads both by `list` and the catalog half. When a part's catalog graph entry no longer
declares the stream the keeper follows for it (its findings moved to a template, §611, or to an old section, §612),
the keeper drops that source (the grants part its consumer, §690), keeps the part's open conditions, and asks `findings-asked` on the
new stream once it is a source; the part's statement there settles them (§384). §515 is corrected so.
[mesh-contracts DQ-12; D-7's COND9.] Why: the grants part makes the consumers (§386), and no binding names one; a
moved stream must neither raise twice nor leave a condition open (review: health and events 1). How it is
checked: whole (keeper, bus module), a part declaring finding events is asked once its entry is `created`; a part
whose findings move keeps its condition raised once and clears it from the new stream.
- §664 **`backed-up` needs no contract change.** (§563) The backup holder's answer carries a put-off item's `act` and
since when in its own fields. [mesh-contracts DQ-14.]
- §665 **`attempt-changed` is keyed by condition and attempt.** (§541, §73) The subject is
`mesh.seat.mesh-healers.event.attempt-changed.<condition>.<attempt>`, each token escaped as a key's name is (§73),
kept last per subject, 7 days and 16 MiB (§541), so two attempts on one condition both stay. At about 0.5 KiB a
message, 16 MiB holds about 30,000 attempts, far more than seven days have seen (an estimate). The stream discards
nothing new: a publish past its cap fails, and the healers say it in their health and write the finding
`attempt-unsaid` (scope the healers, id the condition), cleared by that subject's next publish taken. §541's
subject is corrected so. [mesh-contracts DQ-15.] How it is checked: unit (healers' contract), two attempts of one
condition kept; whole (healers), a full stream raises `attempt-unsaid`.
## Met in develop
- §666 **`nats_snapshot_get`.** Replaced whole by §681 (G-18): the keeper serves the seat `mesh-broker`'s
`snapshot-get` and `snapshot-restore`, the bundle lists by `nats_snapshots`, and a protected stream's snapshot is
never sent (the operator, O-7). [D-1.]
- §667 **Where a trusted change and the seed wait for their warrant.** (§3, §8; ADR 0666 §2, §12; ADR 0259 §6; ADR
0286 §10) [D-2, D-3.]
- **Kept.** The indexer keeps each change of `register`, `policy`, `watch` and `unwatch` awaiting its warrant in its
old `state` section `proposals`, one key `<verb>.<module>` (`<verb>.<module>.<branch>` for `watch` and
`unwatch`, each token escaped as a key's name is, §73), holding the change whole, its `digest` (`sha256:` of the
change in RFC 8785's canonical form), the current ask's id, its caller and when it was asked; the seed's key is
`seed`. Capped at 4 MiB.
- **Asked.** Through `operatorchannelclient`, kind `ask`, level `approve`, its `about` the key, so a newer change of
the same key replaces the open ask (ADR 0666 §12); the ask names every value the change moves (§3) and carries the
change's `digest`, so the warrant approves those exact values (ADR 0286 §10).
- **Heard.** The indexer's manifest `consumes` `operator-channel.decided`, on
`mesh.seat.operator-channel.event.decided.<the indexer's module>`, through a durable consumer. A warrant is
applied only when its ask's id is the key's current ask and its digest the key's, and when the router's own
record of that ask, read under the indexer's module's name on §700's path, holds it with `Warrant.For` (ADR 0286 §26, §27, §36),
never the decided event's body alone: it writes the version by
compare-and-set and ends the key `applied`. A warrant of a replaced ask writes nothing, said in its journal. A
Decline, an expiry or a refusal ends the key with that outcome; the seed's keeps `seed-waits` (§8).
- **Ended keys stay readable.** An ended key keeps its outcome, its ask and when, until the next change of the same
key replaces it.
- **A restart.** At its start the indexer reads each open key and the router's record of its ask on §700's path: a decided one is applied or ended as above, an open one waited on, and one the router no longer holds ended
`lost`, said.
- **Its tools.** `indexer_proposals` (class `read`) lists the open and ended keys with their asks and outcomes;
`indexer_proposal_drop` (`key`, `why`; class `act`, in the hand-act log) cancels the key's open ask and ends it
`dropped`; `indexer_seed_again` raises the seed's ask again from its key (§8).
How it is checked: whole (indexer), a `policy` applied on its warrant after a restart while open; a decline ends
the key `declined`, read by `indexer_proposals`; two `policy` changes of one module leave one ask, and the first
ask's late warrant writes nothing; a dropped key's ask is cancelled.
- §668 **`manifest-accepted`'s `problems`.** (§500; ADR 0579 §1) The event gains `problems`, a list of strings
(§692): each problem the indexer's re-judgement finds in a manifest it keeps accepted because its digest is
unchanged (ADR 0579 §1), the same items its verdict writes as warnings; an empty list when none. It is defined in
the seat `mesh-catalog`'s definition in mesh-delivery's contracts, a row of its contracts change. [D-4.] How it is
checked: unit (indexer), a manifest a stricter validator faults is accepted with its problems, and a mended one
says none.
- §669 **A have whose parts differ says the phase `held`.** (§524) §524 holds as written: the phase `held`, its detail
opening `parts-differ:` and naming each digest that differs, and `wait-held` with the reason `parts-differ`; the
build as made (H-8) stands. [D-6.]
## The builder's forms
- §670 **The key codec of the catalog half is a package of its own, which reaches no bus.** (§614) The codec of a
catalog key's parts (`KeyPart`, `ParseKeyPart`) is mesh-delivery's contracts package
`contracts/go/clients/meshcatalogkey`, which imports neither mesh-sdk's `views` nor the bus client;
`meshcatalogclient` keeps the wiring of §614 and imports it. The builder's `builds` package imports
`meshcatalogkey` alone, so it reaches no bus. [D-8.] Why: a package that only reads and writes keys needs no bus,
and the builder's `builds` package is held to that (its test `TestThePackageReachesNoBus`, red on main too). How it
is checked: that test, and one in `meshcatalogkey` that it imports no bus.
- §671 **`build.ended` names the node that keeps a kept build in `kept-on`.** (§271, §379, §526) For a kept build the
builder's `build.ended` carries `kept-on`, the build node that keeps it, once for the build, beside `made`, the name
§526 gives the want's field; every artifact of the build is kept on that node, and each artifact in `made` keeps its
digest as now. A build not kept carries no `kept-on`. [D-9.] How it is checked: unit (builder), a kept build's
`build.ended` names its node, and the assigner writes it into the want (§526).
- §672 **The builder's toolchain wait.** (§267, §276, §472, §557, §629, §633) The builder holds an entry while its
fingerprint names a toolchain version of its language other than the one the builder's newest `toolchain-agreed`
of that language names (§267).
- **Its state.** Each wait is kept in the builder's old `state` section `waits`, one key `<module>.<fingerprint>`
(each token escaped as a key's name is, §73), capped at 4 MiB, deleted when the wait ends.
- **Its events.** It says `wait-held` and `wait-ended` on its own old `streams` section `waits`, on
`mesh.stream.mesh-delivery-builder.event.wait-held.<module>.<fingerprint>` and `.wait-ended.<module>.<fingerprint>`,
kept last per subject, 7 days, 16 MiB; the body the fields of §629's wait with `step` `build` (§702), `on`
`toolchain:<language>@<version>`, `module` and `fingerprint`, and `wait-ended` its `how` (`ran` when a
`toolchain-agreed` names that version, `released` by `builder_release`). At its start it says `wait-held` again
for every key of `waits`. G-4's table reads so.
- **Its bound.** 60 minutes, its setting `toolchain-wait-minutes`; past it the builder writes the finding
`toolchain-waits` (scope `build`, id `<module>.<fingerprint>`, §702), cleared when the wait ends.
- **Its tools.** `builder_waits` reads the state, and `builder_release` builds the entry with the running build
module, written in the hand-act log (§276).
[D-10.] How it is checked: whole (builder, with small settings), an entry held until `toolchain-agreed` names its
version, then built and ended `ran`; one released by hand ended `released`; one past its bound raises
`toolchain-waits`; a restart says the open wait again.
- §673 **`package-published`'s one `dependencies` list holds both kinds.** (§374, §466) The contract's `dependencies`
in each artifact of `package-published` is §466's `resolved` and `toolchain` in one list: each item `{kind,
part, version, digest}`, kind 1 for each `resolved` package (its part the package's artifact reference, §504) and
one item of kind 3 for the toolchain (its part the agreed toolchain's part, its digest the building node's, §466).
The indexer writes §374's entries of kinds 1 and 3 from the items of those kinds. [D-11.] How it is checked: unit
(`mesh-builds`' contract), a list of both kinds read; whole (indexer), the release's kind-1 and kind-3 entries.
- §674 **`<module>_first_views` says the part's own build.** (§205, §424, §519, §618) Both answers of a part's first
views, the bundle reader's and the process's status tool's, gain `own-build` beside their list: the fingerprint the
node-engine composed into the part's environment (§519), or `unknown` when it carries none (§618). A process writes
it at its start at the key `own-build` of its state `first-views`, which its tools bundle's status tool reads.
[D-13.] How it is checked: unit (`go/views`), both answers with a fingerprint and with none.
## The indexer's entries
- §675 **`entry` finds an entry by its fingerprint, and keeps one while a want or a running build names it.** (§443,
§199, §556, §614) [D-14.]
- **By fingerprint.** The indexer keeps, in its old `state` section `entry-index`, one key per build-input
fingerprint, holding the digest of its entry's object (§179, §443); it writes the key when it first stores the
entry and deletes it when it removes the object. `entry` reads the key and then the object. Capped at 8 MiB (an
estimate: about 0.1 KiB a key for some tens of thousands of builds).
- **Its callers.** `entry` is of class `read`, so it takes `read`'s default (§391, §610), its invokers among them
(§614).
- **Kept while named.** The indexer consumes the node-engine seat's `running` and `mesh-wants`' `wanted`, read whole
at its start as §556 has a stream kept last per subject and then followed by a durable consumer, both in its
`consumes`. It keeps in its old `state` section `named` one key per fingerprint with the set of
`<node>.<module>` whose want or running build names it, written by compare-and-set, capped at 4 MiB. An entry
object is removed only at the first revision after no kept revision lists it and its `named` set is empty; a
`wanted` or `running` that moves on removes its `<node>.<module>` from the old fingerprint's set.
- **A node that leaves.** A `running` stays on its stream for ever, so a member a `running` put in the set leaves it
also when the want of that node and module is removed (`wanted` saying it removed: the node left or the module was
unplaced), whatever that node last said running.
- **Its tools.** `indexer_entries` (class `read`) reads a fingerprint's key, its object's digest and its `named` set;
`indexer_named_drop` (`fingerprint`, `member`, `why`; class `act`, in the hand-act log) removes one member.
How it is checked: unit (indexer), an entry no revision lists is answered by `entry` while a want names it and
removed after the want moves on; a member whose want is removed leaves the set; `indexer_named_drop` removes one;
a restart reads `named` whole before it removes anything.
- §676 **Each entry carries its artifacts' package names.** (§491, §478, §504, §412) At take-in the indexer reads each
artifact's package name from the revision's own files at the artifact's path (the `module` of its `go.mod` for a Go
package, the `name` of its `package.json` for an npm package, the manifest's image name for an image) and writes it
into the entry's artifact as `package`, `{ecosystem, name}`. The manifest validator judges §491 from the entries'
`package` fields, and the builder's mapping (§478, §504) reads them there. [D-15.] How it is checked: unit
(indexer), a Go and an npm artifact's names read into their entries; unit (manifest validator), two entries
carrying one name refused, naming both.
@@ -0,0 +1,559 @@
---
layer: to-be
status: in-progress
design: 47
milestone: G
updated: 2026-11-11
decisions:
- "ADR 0674"
- "ADR 0673"
- "ADR 0672"
- "ADR 0668"
- "ADR 0667"
- "ADR 0666"
- "ADR 0660"
- "ADR 0656"
- "ADR 0626"
- "ADR 0619"
- "ADR 0618"
- "ADR 0596"
- "ADR 0595"
- "ADR 0579"
- "ADR 0573"
- "ADR 0448"
- "ADR 0436"
- "ADR 0429"
- "ADR 0363"
- "ADR 0286"
- "ADR 0283"
- "ADR 0259"
---
# 47, milestone G: the fix round of batches 6 and 7
One file of milestone G's part of [design 47](../47-delivery-from-commit-to-delivered.md); the part opens at
[G.md](G.md). Its design lines are numbered across every file of the part and cited as "design 47 G §n".
*§677 to §690 come from the fix rounds of the review by architectural domain of §587 to §676 (lead-47's inputs of
2026-11-11): the approval mark once no verb names its callers (the operator's answer of about 16:10 CET, §610); the
keeper's snapshots, with the operator's answer O-7 of 20:22 CET (§681); the recovery module's verbs, the entries the
node-engine keeps and the grants part's first composition (§682 to §684); the gate before a marked verb's running
(§677, the operator's O-8); develop's D-16 to D-49, of internal form only; and the operator's O-9 (§705). The rounds' other fixes are written in the lines they correct.*
## The approval mark, with no named callers
- §677 **A marked verb a module's process serves is one call with a gate before its running, asked, checked and
claimed by its holder alone.** (§610; ADR 0676; ADR 0286 §10 to §13, §24, §26 to §31, §35, §36, §38, §43; ADR 0259
§3, §6) Once no verb names its callers (§610), a module's process may call a marked verb (ADR 0286 §1, §2)
directly. Design 46 7-approval-mark's gate wraps only the handlers the tool runner serves (its §20); a marked verb a
module's process serves is judged by that process, as ADR 0286 §43 has the controller judge its own verbs, whoever
calls it. The operator, 2026-11-11 about 20:40 CET at lead-47's terminal (O-8): "the tool call should be the same,
just with a 'gate' between the call and the execution"; so a module's call of a marked verb is one call:
- **Only Go holds the gate, and every marked verb is `by-caller`.** The gate is mesh-sdk's Go seat server's; the
manifest validator refuses a TypeScript process that serves a marked verb, and the seat generator makes every
marked verb `by-caller` (§697).
- **Refused before anything is kept.** A call whose arguments hold anything shaped like a secret or pass 16 KiB is
refused at once, asking nothing and keeping nothing (ADR 0286 §12).
- **The holder keeps the call and asks, under its own module's name.** Otherwise mesh-sdk's seat server makes the
act as ADR 0286 §10 and §11 make one (the address as called, the holder's own node and build, the seat and verb,
every argument, digested with `operatorchannelclient.ActDigest`), and finds or raises its ask through the key
`act.<digest>`: it writes that key with the bus's create, naming a new ask, and raises the ask through
`operatorchannelclient` under the holder's module's name at level `approve` (ADR 0286 §13, §15 to §23) only when
the create succeeds; an instance whose create fails reads the key and takes that ask, so two instances raise one
ask; a key whose ask has ended is replaced by compare-and-set on its revision. When the call says `wait: held`
(below), the holder keeps it as a waiting call of its own, `call.<id>`, `<id>` 128 random bits, holding the call
whole, the caller the bus proved and its ask (ADR 0286 §24), and answers at once `{asked: <ask id>, call: <id>}`.
- **A second call of the same act.** Every call is kept as its own `call.<id>`, whoever makes it; one made while
that act's ask is open takes the same ask (ADR 0286 §13, §23, §38) and its answer names that ask and its own
call. On the Approve the oldest kept call of the act runs; each other kept call of the act is asked anew at once,
as a new ask of that act (ADR 0286 §33: a second run takes a second approval), and its end comes when its own ask
ends.
- **On the Approve it runs that same call.** When the holder hears its own `operator-channel.decided` of the ask, it
reads the router's own record of the ask under its module's name, on the one path §700 names, never the decided
event's body nor the caller's word (ADR 0286 §26, §36), holds the warrant to it with `Warrant.For` and
`Option.Performs` against the kept call (ADR 0286 §27, §28), claims it once (below), runs the kept call, once, and
records the run in the hand-act log of the seat `mesh-hand-acts` as a kept call until it is taken (ADR 0286 §35,
ADR 0668 §1). The kept call runs only within 30 minutes of the choice; heard later (the holder away meanwhile),
it ends `expired` and runs nothing (ADR 0286 §29). After a Decline the same act is not asked again for 10
minutes: a call of that act within them ends `declined` at once, with no ask (ADR 0286 §38).
- **The caller hears the outcome on its own call's subject.** The holder says the call's end on its seat's event
`gated-ended`, subject `mesh.seat.<seat>.event.gated-ended.<caller>.<call>`, `<caller>` the account the bus proved,
escaped as a key's name is (§73): `{call, ask, act, verb, outcome, answer, error}`, `outcome` one of `ran` (with
the verb's `answer`, and in its own field `approval` the line naming the approval, ADR 0286 §34), `declined`, `expired` and `refused` (with the router's or the check's words); a decline or
an expiry runs nothing and says so. Kept last per subject, one day, and no argument is in it: a caller down for
longer than a day loses that outcome, and reads the holder's `approvals` verb (below) for it. Every seat
definition with a marked verb gains the event through the seat generator; the grants part grants hearing
`gated-ended.<caller>.>` to that caller alone, from its `invokes` of the marked verb by name, as it composes a
`by-caller` verb. mesh-sdk's seat client, in `go/seats` beside the seat server, sends `wait: held` on every call
of a marked verb, waits as a kept wait (ADR 0668 §1) on its own `<call>` alone and reads it at its start, so a
restart of either side loses no outcome; it makes no second call. A module may not mark its call of a marked
verb safe to repeat (ADR 0668 §2): only the wait on `gated-ended` is kept. The calling module holds no warrant,
and the holder asks once per call; `mesh-delivery.release` stays unmarked (ADR 0286 §46), so ADR 0286 §47's
question is closed.
- **A person through the tool runner keeps ADR 0286 §37's path for now.** An MCP call cannot be held for an ask's
60 minutes (O-8); this "for now" is tracked by issue 417. A call that does not say `wait: held` (the tool runner sends none) is answered with ADR 0286
§37's refusal naming the ask and keeps no call, its ask found and written through `act.<digest>` as above, so the
same call made again after the Approve finds that ask, is checked as above, claimed and run. `wait` is no
boundary: either path runs the act only on the operator's warrant for that act, claimed once, so a module that
leaves it out only takes the person's path.
- **The holder claims, once, in its own state.** The holder's template state `approvals` (a `requires` entry, 48
hours to live, 64 MiB) keeps `call.<id>`, `act.<digest>` and `claimed.<ask id>`, the claim, written with the
bus's create, which refuses a key that exists, so a warrant runs one call, on one instance or two (ADR 0286 §30,
§31). At its start the holder reads every kept call and acts on each ask already decided as above. A call kept
past its ask's 60 minutes with no decided heard raises the holder's finding `gate-unanswered` (scope the holder,
id the call), cleared when its end is said. The manifest validator refuses a module that serves a marked verb from
a process and declares no such entry. A state that cannot be read or written refuses every marked call of that
holder, said as ADR 0286 §40 has it.
- **Its read.** Every seat with a marked verb gains the verb `approvals` (class `read`, its default), served by the
seat server: the kept calls with their caller, ask and state, the `act.` keys and the claims, never an argument
of a call it was not asked by that call's caller.
- **A holder that does not check is never reached.** The grants part composes, for a module's process that serves
a marked verb, the subscription of that verb's subjects only when the catalog graph entry of the build its want
names (heard by `wanted`, §344) declares that `approvals` entry, which only a build of G's seat server passes the
validator with, so a G holder is granted before it subscribes; otherwise it composes the subscribe deny design 46
7-approval-mark §6 composes for a runner that does not state `approval-mark/1`, named in `withheld` with "its
holder checks no warrant". So a holder of before G serves none of its marked verbs until its G version runs, and
nothing runs unchecked. This changes ADR 0286 §7 and §8 for a process holder (ADR 0676).
Why: the operator's answers O-6 and O-8; the holder alone can read the router's record of an ask made under its
own name (review rounds 2 and 3). How it is checked, as cases of the seat server's one whole test (a fixture marked
verb, its holder and a module's process): one call answered `asked` and run once on the Approve, its outcome heard
on `gated-ended` by that caller and no other; a repeated identical call kept as a second call, asked anew after the
first runs, each waking on its own `<call>`; two callers of one act, one ask, one run, the second asked anew; two
instances of the holder raising one ask and one claim, a race of two at `claimed.<id>` running once; a decline and
an expiry run nothing; a restart of the holder between the ask and the Approve runs the kept call once; a warrant
replayed runs nothing; a call within 10 minutes of a Decline ended `declined` unasked; a kept call heard 31
minutes after its choice ended `expired`; a secret-shaped argument refused unkept; a call with no `wait` refused with the ask and run
on the same call made again; unit (grants part), a holder whose wanted entry declares no `approvals` composed with
the deny and named in `withheld`, one that declares it granted before it runs.
## Develop's D-16 to D-18
- §678 **The forms of a finding's `owner` and `waiting-on`.** (§451, §540, §580) Each is one of three forms, written as
patterns in the finding layout's definition: a session `^[a-z0-9][a-z0-9._-]*@[a-z0-9][a-z0-9-]*$` (who@node), a seat's
name `^[a-z][a-z0-9-]*$`, or a commit's author `^forge:[A-Za-z0-9._-]+$`. mesh-sdk's finding writer (§580) refuses any
other, naming the field; the contract's fixtures read each form. [D-16.] How it is checked: unit (writer), each form
written and another refused.
- §679 **`reserved`'s answers.** (§582, §586) `reserved` answers `{module, version, artifacts, released}`, `released` a
boolean (as §530's `none-yet`), or `{error}`, a string; `module`, `version` and `artifacts` are required only without
`error`. [D-17.] How it is checked: unit (`mesh-builds`' contract), the three answers read.
- §680 **Where the seat generator and the build modules' conformance kit live.** (§464, §495) §495's "the seat
generator is imported by no running module" reads "by no running module but through `steprun`'s regenerate and
compare". The build modules' shared conformance kit is `sdk/steprun/conformance` in the delivery SDK, which
mesh-catalog's build modules and their checks import. [D-18.] How it is checked: the code review.
## Round 2 of the review: the keeper's snapshots, the recovery module's verbs, the entries the node-engine keeps
- §681 **The keeper's snapshot is listed by a tool, sent by the keeper, and never sent when it holds a protected
stream.** (§554, §567, §610; ADR 0675; ADR 0626 §1, §3, §4; ADR 0436 §3, §5; ADR 0672 §1, §8; ADR 0667 §12) This
replaces §666 whole. [D-1; review rounds 2 and 3.]
- **When it is taken.** The bus module's keeper, taking the snapshot before a `drop` or `recreate` (ADR 0595 §5),
writes into that request's record in its state `requests` the snapshot's `path` (under its snapshots directory),
`size`, `digest` (`sha256:` of its bytes) and `protected`, true when the entry has a stream ADR 0436 §3 lets no
other account hear (the mail passwords' among them), before it touches the live data. A snapshot it cannot write
whole and record so, or one that would pass the directory's cap `snapshots-cap` (a setting, 20 GiB unless set),
makes the `drop` or `recreate` refuse, naming why, before anything is removed. Only the snapshots it recorded
count against the cap; one of before G does not.
- **Its fill.** Past 80 % of the cap the keeper writes the finding `snapshots-filling` (scope the bus module, id
`snapshots`), naming the fill and the cap, and clears it once the fill is back under 80 %, each judged at every
snapshot taken and removed and at its start.
- **Who deletes it.** The keeper alone, a snapshot and its record kept `snapshot-keep-days` (90 unless set, ADR
0667 §12) after its request: it removes those older at its start and at each new request, each removal written in
its journal; nothing waits on a timer, so one may outlive its days until then, which `nats_snapshots` says as
`expired`. By hand, the seat `mesh-broker`'s verb `snapshot-remove` (`request`, or `expired: true` for every
expired one; `why`; class `act`, its default; `by-caller`), written in the hand-act log first, removes one now; a
snapshot of before G it removes only by `request`.
- **Listed.** The tools bundle's `nats_snapshots` (class `read`) reads the keeper's state `requests`, as ADR 0626
§4 has the bundle read it, and lists each snapshot: its request, module, local name, stream, `size`, `digest`,
`at`, `protected`, `expired` and `recorded`.
- **Sent by the keeper.** The keeper's process serves the seat `mesh-broker`'s verb `snapshot-get`, `by-caller`,
class `read`, taking its default (§610). It takes `request` and `call`, 128 random bits the caller chooses, and
answers `{request, module, local, stream, size, digest, at, chunks}`, then sends the file in chunks of at most
512 KiB on `mesh.seat.mesh-broker.chunk.snapshot-get.<call>.<n>`, each acknowledged by the caller on
`mesh.seat.mesh-broker.ack.snapshot-get.<call>.<n>` within 30 seconds before the next, a missed one ending the
send, said in its journal. The verb says so in its seat definition by the field `chunks: true`, a word of the
definition format that §494's second reader release reads (§621, §697), costing no rebuild beyond that release's.
From it the grants part composes the holder's publish of `mesh.seat.<seat>.chunk.<verb>.>` and subscribe of
`.ack.<verb>.>`, and the reverse for an account that names the verb by name in `invokes`; a `*` entry grants no
chunk subject. Since every such account may ask any snapshot it could hear by the read default, hearing another's
chunks gains it nothing it could not read with the read default, and a protected snapshot's chunks are never
published. A caller granted no chunk subjects (a session through the tool runner) is answered
the fields alone, with `chunks` 0, and no file is sent. The verb answers `error`: `snapshot damaged`, naming the
request, when the file's digest differs from the recorded one; `snapshot missing` when no file is there;
`snapshot protected` when `protected` is true; and `snapshot taken before milestone G` for a request record that
holds no `digest`. The read default widens who reads a dropped or recreated module's data beyond its owner (ADR
0595's kept data reached only by its owner), for this snapshot alone (ADR 0675).
- **A protected snapshot is restored in place.** The operator, 2026-11-11 20:22 CET at lead-47's terminal (O-7):
the stream snapshot the keeper took before a `drop` or `recreate` of a protected stream is never handed out, and
is restored only in place by the bus module on the recovery module's request; the recovery module's own asides
of restricted streams stay as ADR 0672 §8 and §9 have them. The keeper's process serves `mesh-broker`'s verb
`snapshot-restore`, `by-caller`, class `act`, taking its default (§610): it takes `request`, `verify` and `why`,
answers at once `accepted` with an act id, `busy` or `error` (as §691's form), checks the file against its
recorded digest, and with `verify` true says that and restores nothing; otherwise it refuses `names live` while a
live object of the stream's name exists, before anything else, and restores the snapshot's stream under its own
name, bounded at 60 minutes. It says the outcome on `mesh.seat.mesh-broker.event.snapshot-restored.<request>.<act>`
(`{request, act, outcome, error}`, kept last per subject, 90 days), so a retried restore is told apart from an
earlier one. A restore that fails, passes its bound or is cancelled removes the keeper's own partial stream (it
held no live data, since `names live` came first) and then says `snapshot-restored` with `outcome` `failed`
under that act, before the keeper answers anything more of it; one cut off by the keeper's restart is ended so at
its start. `mesh-broker`'s verb `snapshot-restore-cancel` (`act`, `why`; class `act`, its default, `by-caller`)
cancels a running restore so and answers `accepted`, or `error` when that act has ended. "Protected" means a
stream ADR 0436 §3 restricts. The keeper holds a standing grant of the bus's
stream restore subjects and their chunk subjects for the bus names of the streams ADR 0436 §3 restricts, and only
those, and uses them only on a `snapshot-restore` call.
- **Its use.** `recovery_restore` names a snapshot by `request` (§682). For one that is not protected the recovery
module reads it through `snapshot-get` into `incoming/<request>` in its own data directory, refusing before the
first chunk when `size` would pass `points-cap` (§554), checks the bytes against `digest` before it takes any aside
(§567), restores it itself (§516), and deletes the file when the act ends, done or failed, and at its start any
file left there. For a protected one it asks `snapshot-restore` with `verify` true, then sets the live data aside
(§516), then asks `snapshot-restore`, and waits on `snapshot-restored` of that request and the act it was answered,
bounded at 90 minutes, past the keeper's 60. It puts its aside back under the live names (§516) only on that
event's `failed`. At its bound it first asks `snapshot-restore-cancel` and waits on the same event for the
cancel's outcome; only when the keeper answers neither, it puts nothing back, ends `failed` with
`snapshot-restore unanswered` and writes `aside-unrestored` (§561), which `recovery_restore_aside` (§566) clears,
refused while the names are live. Its
account (§554) hears `snapshot-restored`, from its `consumes`.
- **A snapshot of before G.** A request record with no `path` or `digest` was written by a keeper of before G: it
is listed with `recorded` false, `snapshot-get` and `snapshot-restore` refuse it as above, and the keeper of G
neither sends nor deletes it by age: it stays where the keeper before G wrote it until `snapshot-remove` names it.
- **Its record.** ADR 0675, which changes ADR 0626 §1 and §3, ADR 0436 §3 for the keeper's process, and ADR 0672
§1, §8 (its read opened to the read default) and §10 (a protected snapshot restored under the stream's own name by
the keeper), keeping ADR 0436 §3 and §5 for every other account.
Why: the operator's answers O-6 and O-7; the tools bundle runs as the tool runner and cannot read the keeper's
directory (review rounds 2 and 3). How it is checked, as cases of the bus module's one whole test and the recovery
module's one whole test, adding no test function to either (with small settings): a snapshot of three chunks sent
and restored whole; a missed acknowledgement ending the send; `snapshot missing`; a file altered on disk refused
`snapshot damaged`; a session through the tool runner answered `chunks` 0; a snapshot removed after
`snapshot-keep-days` at the next request; `snapshots-filling` raised past 80 % and cleared under it; a protected
snapshot refused by `snapshot-get` and restored in place by `snapshot-restore`, refused `names live` while its live
stream exists, a retried restore woken by its own act; no chunk of a protected snapshot ever published; a restore
stopped midway leaving no partial stream; the recovery module's bound cancelling first, then restoring the aside; a request record of before G listed `recorded` false and
refused; a `drop` past `snapshots-cap` refused before it removes anything; unit (grants part), only the keeper
publishes the chunk subjects, a `*` entry gets none, and the keeper alone holds the restore subjects of the
restricted streams' names and of no other stream.
- §682 **The seat `mesh-recovery`'s hand verbs: their fields, their answers, and who asked.** (§516, §554, §566, §656;
ADR 0672 §4 to §6, §12; ADR 0386 §8) [D-19; review round 2, seams and tools 8.] Each hand verb is `by-caller`, of
class `act` taking its default (§610), defined in mesh-contracts with fixtures, and takes, beside its own fields,
`why` (required) and `asker`, the caller the tool runner proved and recorded for that call (ADR 0386 §8: who@node,
or `operator`), which the tools bundle's `recovery_*` tool passes and which a module's process leaves out. `asker` is a stamp,
no boundary (as ADR 0626 §4 has it): it is taken from any caller and logged beside the account the bus proved, so
a false one is seen. The
hand-act log's entry names both the account the bus proved and `asker`: the bus's is the boundary, `asker` names
who asked, for the record (as ADR 0626 §4 has the stamp). Each answers at once, as `snapshot-kept` does (§554):
one of `accepted` (the act's id), `busy` (the id of the act that runs) or `error` set alone (§691); the result
comes on the act's ending event (§554, §560), under its `act` id.
- `take` (`module`, `kept`, `fingerprint`): takes a point of that kept data at that fingerprint's form, as
`snapshot-kept` does with no `catch-up`; ends with `ended`.
- `restore` (`module`, `kept`, and exactly one of `snapshot`, a point's name, or `request`, a keeper's snapshot
by its request, §681; `from-backup`, yes or no, only with `snapshot`): restores as `restore-kept` does; ends with
`restored`.
- `retry` (`module`, `kept`): asks again the last failed act of that module and kept data, with its fields;
`error` `nothing to retry` when its last act did not fail; ends with that act's ending event.
- `aside` (`module`, `kept`): sets the live kept data aside without restoring (§516); ends with `set-aside`.
- `discard-aside` (`module`, `kept`, `id`): removes that aside's directory; ends with `aside-discarded`.
- `expire-points` (no field of its own): runs §551's removal of points past their 90 days now; ends with
`expiry-ended`.
- `restore-aside` (`module`, `kept`, `id`) and `cancel` (`act`) as §566 has them; `cancel` answers in the same
form (§691): `accepted` with the id of the act it cancelled, whose ending event follows under that id, or
`error` naming the step the act has begun.
- `restore` and `restore-kept` are two verbs, both kept: `restore-kept` is the node-engine's (§516), taking
`form`, `fingerprint` and `snapshot` and refusing `aside-only` and `discard-aside`; `restore` is the hand verb
above, taking a point by `snapshot` or a keeper's snapshot by `request`.
Why: the developer's D-19; the bundle calls on the tool runner's account, so the log would name the runner, not the
asker (review round 2). How it is checked: unit (mesh-recovery's contract), each verb's fields and answers read
from its fixtures, `restore` with both `snapshot` and `request` refused by the schema's `oneOf` (§701); `retry`
with nothing failed answered `nothing to retry`; `cancel` of a begun act refused naming its step; whole (recovery module), a hand act's log
entry names the tool runner's account and the asker.
- §683 **The node-engine keeps the entries and wants it composes from in its composition state.** (§421, §614,
§627, §422, §693; ADR 0593 §6) [review rounds 2 and 3.] The node-engine, a foundation part (§627), composes the bus
module and the indexer that serves `mesh-catalog.entry`, so it never relies on `entry` answering at a restart:
- **Kept on the node.** Each entry it holds (§614, *An older build's entry*) and the wants of its last composition
are kept in its composition state on the node (ADR 0593 §6), beside the composition, written whole before the
composition that uses them; an entry is removed when no want or `running` it holds names its fingerprint, and a
module's want when `wanted` says it `removed` (§693). At its start it reads them before it asks anything, so a
restart composes the bus module and the indexer while both are down; only a fingerprint it holds no entry for is
asked of `entry`.
- **They give way to the bus.** The kept wants stand only until the seat `mesh-wants`' stream answers its read of
§556; from then the stream's wants are the ones it composes from, so a revert then an upgrade never composes from
a want older than the stream's. Until its first live answer its have says it composes from kept wants and
entries, and a kept part it cannot read is said in its have and its log, that module composed as its running
composition stands and its entry asked of `entry`.
- **Its first start.** The node-engine of G names `seat:mesh-catalog.entry` in its `invokes` (§614), so by §558's
kind 6 its G version runs on a node only after the indexer of G answers `entry`; the bus module and the indexer
of G are composed by the node-engine of before G, as on main. A node-engine of G at its first start, or on a newly
adopted node, holds nothing kept and asks `entry` of the indexer that runs.
- **Its rows and its ask again, on its seat.** It serves no module tools (§422): its kept entries and kept wants
are rows of the seat `node-engine`'s verb `first-views` (§698), and the seat gains the verb `entry-again`
(`fingerprint`, `why`; class `act`, taking its default, §610), both in mesh-contracts with fixtures, in place of
§614's `<module>_first_views` and `<module>_entry_again` for it.
Why: the node-engine composes before the bus answers (§627). How it is checked: whole (node-engine), a node-engine
restarted with the bus module and the indexer stopped composes both from its composition state; a kept part made
unreadable is said and asked again; a revert then an upgrade composes from the stream's wants once it answers;
unit (the seat's contract), `first-views` rows and `entry-again` read from fixtures.
- §684 **The grants part composes a want's account before any branch view.** (§614, §507, §558, §374) [review round
2, seams and tools 1.] The grants part follows the branches as §614 has it, from the gate's `source` with no branch
(named in its `invokes` in the long form, `{verb: seat:mesh-delivery.source, fields: [branches]}`, §558), so its
full pass waits on the gate's G version (kind 6), while the gate's G version waits on the grants part composing its
account (kind 5). So, until every branch it follows has its first view, the grants part keeps the user list it last
wrote and composes, for a want whose account it holds none of or whose fingerprint changed, that one account from
the want's catalog graph entry by fingerprint (§614, *An older build's entry*) and the assigner's views alone, as it
composes any account, every marked subject in it denied while the marks are unread (design 46
7-approval-mark §19); it writes that account into the user list it keeps, and says in its log and in
`<module>_first_views` that it composed it before its first views. Its full pass, once the first views are in,
composes every account again as always. So the gate's G version is granted before it answers, and no cycle stands.
- **The indexer's own account.** `entry` is served only by the indexer of G, so the grants part of G names
`seat:mesh-catalog.entry` in its `invokes` and, by §558's kind 6, runs only after the indexer of G answers;
the indexer of G's account is composed by the grants part of before G, as on main, so no cycle runs through the
indexer.
How it is checked: whole (grants part, gate), with the gate of before G running and a want of the gate's G
version, the grants part composes that account before any branch view, and its full pass once `source` answers;
whole (delivery plan), G's indexer is composed while the grants part of before G runs, and the grants part's G
version is ordered after it.
## Develop's D-20 to D-23
- §685 **A condition carries `owner` and `waiting-on`.** (§451, §540, §678) [D-20, mesh-catalog COND7.] The seat
`mesh-conditions`' definition gives a condition, in its state `conditions` and in every condition event, the
fields `owner` and `waiting-on`, both optional, beside `resolver`, which keeps its own meaning (who resolves it: `self`,
`operator`, `agent` or `healer:<name>`). Each takes §678's three forms, refused otherwise, naming the field. The
keeper sets both from the finding when it raises the condition, and changes them only as §540 says (the two hours'
handover to `operator-channel`, and `conditions_owner`), saying each change as it says any change of a condition.
A condition kept from before G carries neither and is read as having none; the keeper writes them into it at its
next change.
How it is checked: unit (mesh-conditions' contract), both fields read in the state and the events in each form;
whole (keeper), a `pipeline-failed` raised with the author's `forge:` owner and changed to `operator-channel`.
- §686 **The generated finding type carries its accessors; the writer takes it through an interface.** (§439, §640)
[D-21.] The seat generator emits, beside each Go type generated from the finding layout of `mesh-conditions`'
`bodies`, in the same generated package of mesh-contracts, methods on its pointer: one getter and one setter per
field the writer reads or fills (`kind`, `scope`, `id`, `at`, `since`, `severity`, `summary`, `headline`, `source`,
`subject`, `observer`). mesh-sdk's Go writer defines, in `go/views`, the interface `views.FindingBody` with those methods and takes a
`views.FindingBody` and a constructor of one (`func() views.FindingBody`, the generated `New`); it uses no generics. In
TypeScript the generated type is structural: the writer's interface names those fields as properties, set by
assignment, and no setter is generated. How it is checked: unit (seat generator), the methods generated; unit
(writer, both languages), a generated body written, its `observer`, `at` and `since` set by the writer.
- §687 **mesh-sdk lays out the views reader's findings itself.** (§639, §686) [D-22.] The reader's four findings are
laid out by mesh-sdk, through §686's constructor and setters, and the part supplies only the constructor: `kind`
the finding's; `scope` the writer's own, as every finding it writes; `id` the view's name
`<owner>.<state>.<branch>`, escaped as a key's name is (§73); `severity` `warning`, but `urgent` for
`view-too-large`, which the part cannot read past; `source` `views`; `subject` `{scope: module, id: <the part's
module>, machine: <its node>}`; `summary` and `headline` fixed texts naming the view and what it waits on. The
part-supplied body function goes. How it is checked: unit (`go/views`, and the TypeScript follower), each of the
four written with those fields.
- §688 **The views reader's four kinds are always its writer's.** (§580, §639) [D-23.] The writer of a part that
holds a views reader always owns `view-waits`, `first-view-unanswered`, `view-unsaid` and `view-too-large`, added
to its stated kinds when `Kinds` is set, so its statement covers them (§580). A module whose bundles share one state
`findings` holds a views reader in one bundle only (in G the forge module's TypeScript tools, §196), which the code
review checks. How it is checked: unit (writer), a writer made with `Kinds` states the four with them.
- §689 **The hand-written finding helpers go; mesh-sdk's writer is the one writer.** (§461, §503, §640, §660) [D-24,
mesh-contracts K15.] With the state `findings` holding `#/bodies/Finding` under `<scope>.<id>.<kind>` (§660), the
finding helpers of mesh-contracts' `meshconditionsclient` go, as its `findings` package does (§503): `CheckFinding`,
`FindingFor`, `FindingWriter` and `FindingState`, `FindingVersion`, `Observation` (with its `Key` and `Finding`),
`EncodeFinding`, `DecodeFinding`, `Says`, `FindingBucket`, `FindingsBucket`, `Raise`, `Clear` and `CallUnanswered`.
`CheckFinding`'s rules (the key's parts, the kind, §678's forms) live only in mesh-sdk's writer, which refuses a
finding breaking them before it writes, and which exports that check for the keeper as `views.CheckFinding(b views.FindingBody) error`, in `go/views`, returning the
first rule broken, naming its field (§686; D-35). What concerns conditions
stays: `Key`, `EventFor`, `ShowKeeper`, `SilenceAction`, `Order`, `SilencedAt`, `Escalated`, `ResolverHealer`, the
scope, resolver and severity words, `KindCallUnanswered`, `CallUnansweredAfter` and the package `heard`. The
importers move in G's change: every part that writes findings writes them through mesh-sdk's writer and the
generated type (§686), its texts written into `summary` and `headline` where `Says` made them: in mesh-catalog
`artifact-store-tools`, `claude-code`, `distribution` and `distribution-tools`, `package-registry`, `mesh-build-go`,
`mesh-config`, `mesh-healers`, `mesh-provisioner`, `mesh-self-check`, `mesh-vault`, `mesh-watcher`, `object-store`
and `nfs-server`; in mesh-delivery the assigner, the builder, the checker, the indexer and its tools, the teller,
the status view and the watchman; in `mesh-host` the node-engine; in mesh-tools `node-tools/internal/firstview`. The
keeper (`mesh-conditions`) reads the findings others write into the generated type and judges them with mesh-sdk's
exported check. How it is checked: the code review finds none of the removed helpers at G's heads; unit (writer),
a finding breaking each rule `CheckFinding` held refused before it is written.
- §690 **Who drops the keeper's consumer of a moved finding stream, and the gate's `source` in mesh-contracts.**
(§386, §611, §612, §614, §663, §684) [D-25, mesh-catalog COND9.]
- **The grants part drops it.** The grants part, which makes the keeper's consumers (§386), deletes the keeper's
durable consumer on a part's stream at its first composition from a catalog graph entry of that part that no
longer declares the stream, and makes the consumer on the new stream from the same entry, as §386 does for a
module that declares the events later. The keeper deletes no consumer: when it hears, through its catalog half
reader (§614), the part's new entry, it stops reading the old source and keeps the part's open conditions.
- **The keeper takes the new source.** For a template stream on the seat `mesh-broker`'s `created` of the part's
new `finding-events` entry, for an old section's stream from the catalog half, as §663 has it; it binds the
consumer the grants part makes as a kept call (ADR 0668 §1: a call made again on its schedule until it succeeds,
kept in the keeper's state), so a consumer made after `created` is bound once it exists, the source said waiting
in its log meanwhile; once bound it asks `findings-asked` there once (§663). Past 10 minutes unbound it writes the
finding `consumer-unbound` (scope the keeper, id the part's module), cleared once bound.
- **The message on a template stream.** A finding event on `mesh.bus.<module>.finding-events.<event>.<node>` is
mesh-sdk's event envelope that every event publisher writes, its body under `body`; the keeper reads the module
and the node from the subject's tokens and the event's name from `<event>`. [D-36.]
- **The gate's `source` in the seat's definition.** The seat `mesh-delivery`'s definition in mesh-delivery's
contracts (built there, not in mesh-contracts) gives
`source` the optional input `branch` and two answers: with `branch`, that branch's source revision, as on main;
without, `{branches}`, the list of the branch names of the `source.<branch>` keys the gate keeps (§614), the
field `branches` that §684's long-form `invokes` names; with a fixture of each.
How it is checked: unit (grants part), an entry whose finding stream moved composes the old consumer's deletion and
the new one's making; whole (keeper, grants part, bus module), a part whose findings move to a template is bound on
the new stream after its consumer is made, its condition raised once, and `consumer-unbound` raised past 10
minutes while unbound; unit (`mesh-delivery`'s contract), both
answers of `source` read from their fixtures.
## The operator's O-8, and develop's D-26 to D-31
- §691 **The recovery module's contract, as the developer wrote it where no line spoke.** (§516, §554, §560, §567,
§682, §396) [D-30, mesh-contracts K7 to K9.] Kept as lines: every verb of the seat `mesh-recovery` that acts
(`snapshot-kept`, `restore-kept`, `cancel` and every hand verb of §682) answers one form, an object with exactly
one of `accepted` (the act's id; for `cancel` the cancelled act's), `busy` (the id of the act that runs) or
`error` (why) set; `cancel` answers no `cancelled` field [D-32];
the seat's family is `mesh-recovery.events`, 7 days, for any event naming no `keep`; a point's `digests` is a list
of `{name, digest}`, each a bus object of the point and the `sha256:` digest of its bytes (§567); `kept-points`'
`running` is `{act, step, since, waits-on}`, `waits-on` present only while the act waits, and absent when no act
runs. Corrected: `expiry-ended` has no parts, so it is kept 90 days and not last per subject (the definition
validator refuses `last-per-subject` on an event without parts); §554 and §560 read so. How it is checked: unit
(mesh-recovery's contract), each shape read from its fixtures and `expiry-ended`'s `keep` accepted.
- §692 **`problems` are the manifest validator's strings.** (§668; ADR 0579 §1) [D-26, indexer IX28.] The manifest validator's problems are
plain strings and gain no rule names: `manifest-accepted`'s `problems` is the list of those strings, the same the
verdict writes as warnings, in the same order, and `{rule, words}` is not written. How it is checked: unit
(indexer), a faulted manifest accepted with the validator's strings.
- §693 **`wanted` says a want removed.** (§675) [D-27, indexer IX29.] The seat `mesh-wants`' event `wanted` gains
`removed`, a boolean, true when the assigner removes the want of that node and module (the node left, or the module
was unplaced); `fp` is then the fingerprint last wanted there, and the subject is the one that want was said on, so
the stream kept last per subject keeps the removal as that want's last word. The assigner says it in the same
write that removes the want. Every consumer of `wanted` reads `removed` in G's change: the indexer (§675) removes
the member from `named`; the grants part (G-9 §344) composes no account for that want; every §614 reader removes
that want from what names a kept entry; the node-engine (§683) removes its kept want. How it is checked: unit (assigner), an unplaced module's want said `removed`; unit
(indexer), a `removed` empties that member from `named`; unit (grants part, each §614 reader, node-engine), each
reads a `removed` so.
- §694 **An image's package name is its module's and its artifact's.** (§676, §491) [D-28.] An image artifact's
`package` is `{ecosystem: image, name: <module>/<artifact>}`, its module's name and its artifact's name from the
manifest joined by `/`, unique as module names are; the indexer writes it at take-in, reading no file for it, and
§676's "the manifest's image name" reads so. How it is checked: unit (indexer), two modules with an image artifact
of one name carry two package names.
- §695 **An old state section's cap is its `max-mib`.** (§469, §494, §588, §667, §672, §675) [D-29.] A cap the design
gives an old `state` section is declared in that section's entry of the part's manifest as `max-mib`, the word §469
added to the old section and §494's reader release reads: the indexer's `entry-index` 8, `named` 4 and `proposals`
4, the builder's `publish-queue` 4, `acted` 1 and `waits` 4 (§672; its stream `waits` keeps its 16 MiB), each in G's change of that manifest; the grants part
makes the bucket with it (§494). How it is checked: unit (manifest validator), each of those manifests at G's heads
declares its caps; whole (grants part), a bucket made with its cap.
- §696 **A pipeline wait's `on`.** (§534, §498, §266) [D-31, mesh-contracts CT14.] A wait with `step` `pipeline`
has `on` the module of the runner's new version whose healthy run ends it (the holder of
`mesh-pipeline-runner` the new revision names, read from the catalog half), and `words` names that version's
fingerprint. How it is checked: unit (runner's contract), a `pipeline` wait read with its `on`.
- §697 **`by-caller` reaches the mesh only after the reader release.** (§621, §494, §554) A seat definition that
marks a verb `by-caller` (§554's `mesh-recovery`, §681's `mesh-broker` verbs, every verb §677 gates) is read only by
§494's second reader release, merged and delivered before G's merge, whose publish (kind 1) rebuilds every reader
of seat definitions; those definitions reach the mesh only in G's merge, so the order is the release's before G
and needs no edge of the module graph. A reader of before the release that met one would refuse that definition,
which blocks only new claims of the seat (ADR 0636 §3). How it is checked: G's check reads every definition with
`by-caller` through the release's reader.
## The final fix: develop's D-33 to D-38
- §698 **The node-engine's `first-views` rows and `entry-again`'s answer.** (§683, §422) [D-33.] `first-views`
answers `{first-views, entries, wants}`: `first-views` as today; `entries` a list of `{fingerprint, state, since}`,
`state` one of `asked`, `answered` and `not-found`; `wants` a list of `{module, fingerprint, kept-since}`, the kept
wants of §683. `entry-again` answers as `first-view-again` does: `asked` `{v, fingerprint, why, by, at}` and `read`
`first-views`. How it is checked: unit (the seat's contract), both answers read from fixtures.
- §699 **A state a seat offers as a view says so.** (§616; ADR 0571 §6) [D-34.] A state entry of a seat definition
takes the optional field `view: true`; the seat `mesh-module-assigner`'s states `assignments` and `seat-holders`
carry it. The definition format's schema and mesh-sdk's `seatdef` reader gain the field, read first by §494's
second reader release (§621, §697); the manifest validator refuses a `seat:` view in `consumes` naming a state
without it. How it is checked: unit (`seatdef`), `view` read and an unknown field still refused; unit (manifest
validator), a view of a state without `view` refused.
- §700 **A process reads the router's record of its asks from the bus, under its own name.** (§667, §677; ADR 0259
§3; ADR 0286 §26) [D-37.] The one path: the read of the seat `operator-channel`'s state `asks` under
`<module>.`, which the grants part composes for every user of the seat (ADR 0259 §3; design 46 7-approval-mark
§7), on the process's own account; its value carries the ask's `warrant` (7-approval-mark §14). mesh-sdk's
`operatorchannelclient.Client` is given, as its `Record`, a get of that key on the process's own bus connection
(`go/process`), as the tool runner gives it the runtime's `mesh/seat.record`; `Where` and `Warrant.For` are the
same. The indexer (§667, its *Heard* and *A restart*) and every holder of §677 read so; the verb `asks` stays as it
answers. How it is checked: unit (`operatorchannelclient`), `Where` through a process's get; whole (indexer), a
warrant applied from that read.
- §701 **`resync`'s and `retire`'s classes; `restore`'s one of two.** (§610, §682) [D-38.] The seat
`mesh-conditions`' `resync` is of class `act`, and the seat `mesh-vault`'s `retire` of class `warrant`, since its
definition binds it to the operator's warrant on exactly its node, module and name; each takes its class's default.
The seat `mesh-recovery`'s `restore` input holds `snapshot` and `request` under a `oneOf`, so the schema refuses
both or neither, and the holder answers that refusal as `error`. How it is checked: unit (each seat's contract),
the classes read; `restore` with both refused by the schema.
## The final fix: the builder's D-39 to D-44, and two fields
- §702 **The builder's toolchain wait, in the contract's words.** (§672, §629, §546, §604) [D-39 to D-44.]
- **Its finding's scope** is `build` (a scope the finding layout has), id `<module>.<fingerprint>`; §672 reads so.
- **Its step** is `build`, the contract's word; §672's `building` reads `build`.
- **Its body is the contract's wait.** The seat `mesh-wants`' wait in mesh-delivery's contracts gains `owner`,
`on` and `words` (§629, §512 name them), and `module`, `fingerprint` and `how` as optional fields; the builder
says its `wait-held` and `wait-ended` with that type and keeps no body of its own.
- **The build module of a language** is read through the builder's language mapping as it stands
(`buildrun.BuildModuleOf`, from the seats' `acts` rules in the seat list); §546's words name that read.
- **A superseded wait ends.** When the builder holds an entry of a newer fingerprint of the same module, it ends
the older wait with `how` `superseded` and deletes its key.
- **Two build seats report a toolchain.** §604's "the three build seats' `toolchain-reported`" reads "the Go and
node build seats'": the image build seat's builds name no toolchain and it reports none.
How it is checked: unit (builder), a wait said with the contract's type, scope `build` and step `build`; a newer
fingerprint ends the older wait `superseded`.
- §703 **A stream kept last per subject says so in its manifest, and `running` says `stopped`.** (§695, §396, §365)
A manifest's `streams` entry takes `last-per-subject` (yes or no, no unless set), as a seat event's `keep` does
(§396), read by §494's second reader release beside `max-mib`; every old `streams` section the design keeps last
per subject (the builder's `waits`, §672, among them) declares it in G's change. The node-engine seat's `running`
body says a module no longer held by `stopped: true`, with no `fingerprint`, as §365 has it; the seat's definition
gains the field. How it is checked: unit (manifest validator), `last-per-subject` read; unit (the seat's
contract), a `running` with `stopped` read.
- §704 **The indexer's seed, `unwatch`, its asks and its history, in the contract's words.** (§2, §8, §402, §667)
[D-45 to D-49.]
- **The seed's `steps`.** For each artifact of a module's entry the indexer asks `steps` of the build seat whose
`acts` rule `build` has `on` matching that artifact's language or kind (§203, §324), read from the seat list as
the builder's mapping reads it (§702), with `{module, artifact, fingerprint, fetch}`: the artifact's key in the
entry, the entry's build-input fingerprint and the entry's `fetch` of its source, as the entry carries them.
- **Its findings.** `call-unanswered` has scope `call`, id `<seat>.steps`, cleared when that seat answers;
`seed-waits` has scope `seat`, id `mesh-catalog.seed`, cleared by the warrant and kept after a Decline or an
expiry (§8).
- **`unwatch`'s placements** are read through the seat `mesh-module-assigner`'s verb `placement`, named in the
indexer's `invokes`: a placement names the branch when its `build` is a fingerprint listed by that branch's newest
revision and by no other watched branch's.
- **An ask's lifetime** is the router's approval maximum less ten minutes, as mesh-config's asks are.
- **A branch named `candidate` or `entry`** is refused with the reason `reserved branch name`, which the seat
`mesh-catalog`'s contract gains, not `unprotected branch`.
- **`registration-history`** is declared with no age limit and a cap of 64 MiB (`max-mib`), discarding a new
message when full rather than dropping an old one, so no version is lost unsaid: a registration change whose
history write is refused fails, naming the stream, and past 80 % the indexer writes the finding
`registration-history-filling` (scope `seat`, id `mesh-catalog.registration-history`), cleared under it.
How it is checked: unit (indexer), the seed's `steps` input per artifact; `unwatch` refused through a fixture
`placement`; `candidate` refused with `reserved branch name`; a full history refusing a change and naming it.
## The operator's O-9: a delivery waits on another machine's network
- §705 **A machine whose network names an outside cause holds the delivery there as a wait, not a failure.** (ADR
0241 §25 to §27; the main file's *A failure is held on the canary and fixed forward*; §498, §633) The operator,
2026-11-11 about 21:05 CET at lead-47's terminal (O-9): a delivered module's own failing health check is the
delivery's; but while the machine's network statement names an outside cause, the delivery waits on that machine,
says from the start what it waits on, judges again once the cause clears and goes on by itself, and after 30
minutes asks the operator to go on or count it as failed. Where the assigner judges a node's health after a want
moves (the canary rule and its hold):
- **An outside cause.** While the node-engine of that machine says a finding `rewritten` naming a file of a module
the delivery did not move (ADR 0241 §25), or `unreachable` toward another machine (ADR 0241 §25), heard by the
assigner's consumer of the node-engine's finding events (§627's stream, in its `consumes`), a failing health check
of a module the delivery moved there is no failure: the assigner holds that move as a wait, said at once on
`mesh-wants`' `wait-held` with `step` `delivery`, `node` that machine and `on` `rewritten:<owner>` or
`unreachable:<machine>`, and writes the finding `delivery-waits-outside` (scope `delivery`, id
`<node>.<module>`), naming the cause.
- **It goes on by itself.** When that finding is cleared, the assigner judges the module's health there again:
healthy, the delivery goes on, `wait-ended` with `how` `cleared`; failing, it is the delivery's failure, held as
the canary rule has it.
- **The bound and the ask.** Past `outside-wait-minutes` (a setting, §633, 30 unless set) from the wait's start,
the assigner asks the operator through `operatorchannelclient`, `about` `<node>.<module>.<fingerprint>`, with
two options, Go on (the module counted healthy there, so the delivery goes on, `how` `go-on`) and Count as
failed (held as the canary rule has it, `how` `failed`); the ask names the cause and since when; the wait stands
until the answer or the cause clears, which ends the ask. The finding clears when the wait ends.
- **What stays the delivery's or the machine's.** A `rewritten` naming a module the delivery moved is that
module's failure (ADR 0241 §26). A machine's own `network` finding holds the machine as a whole: no want moves to
it until it clears (ADR 0241 §27).
Why: the operator's answer O-9; ADR 0241 §25 had the wait with no bound, which this gives one and an ask, resting on
the record of the outside-cause wait (number to come; its draft is with lead-47), which changes ADR 0241 §25 and
keeps §26 and §27. How it is checked, as cases of the assigner's whole test: an `unreachable` toward
another machine turning a failing check into a wait, said at once, ended `cleared` and the delivery going on when
it clears; one that fails once cleared held as a failure; past the setting the ask, Go on ending `go-on` and Count
as failed ending `failed`; a `rewritten` naming a moved module failing at once; a machine's own `network` moving
no want to it.
@@ -194,12 +194,12 @@ checker's `mesh/check` from a watch of the checker's state `owed`: reads of anot
consumer's backlog: the last event applied, the events waiting, the age of the oldest), `bridge_owed_again`
(perform one id again now) and `bridge_owed_drop` (drop one id with its reason, written in the hand-act log of the
seat `mesh-hand-acts`, and said as `owed.ended` with the outcome `dropped`).
- §169 **The switch carries what is owed.** (§510) At the new gate's start (*The switch carries today's state in, once*) every key
of `owed` and `merges` the old bridge has not performed becomes an act of its record, with an id made of the key and
- §169 **The switch carries what is owed.** (§542, §592) At the new gate's start (*The switch carries today's state in, once*) every key
of `owed` the old bridge has not performed becomes an act of its record, with an id made of the key and
its revision; the new bridge counts as performed every id whose key and revision the old bridge's own bookkeeping
names, so nothing is done twice and nothing dropped. A merge handed to the forge during the switch is such an act:
its merged message finds the record holding it, so it opens no `forge-made` record.
- §170 **A merge made at the forge has a record too.** A merge the forge reports for which no record holds a `merge-pull`
names, so nothing is done twice and nothing dropped. A key of `merges` becomes no act: §542 says it as
`approval-carried`, and §592 has the whole merge path from G's delivery.
- §170 **A merge made at the forge has a record too, until G's delivery** (from it, §151 and §592). A merge the forge reports for which no record holds a `merge-pull`
of that pull request at that head (a person merged it in the forge's own page, as on 2026-11-07 and 11-09) is taken
in like any other merge (*Taken in by the indexer it names*), and the gate opens a delivery record for it, marked
`forge-made`, with no Approve, so its trace and its outcomes on the nodes are kept like every delivery's and no
@@ -211,11 +211,8 @@ checker's `mesh/check` from a watch of the checker's state `owed`: reads of anot
restart and the forge holds it once (the comment and the note by their id); an act said twice is performed once; an
undecodable act is said `undecodable`; `bridge_owed`, `bridge_owed_again` and `bridge_owed_drop` answer, the drop
writing the hand-act log; its account cannot read the gate's or the checker's state. In the gate's: an act left
unanswered is said again at the gate's start under its id and listed by `stalled` after 15 minutes; a group's second
`merge-pull` is said only after the first member's merged message; the switch with a merge handed and not yet
merged makes no `forge-made` record; a merged message with no record's merge opens a `forge-made` record, said as
`transition` with why `forge-made`, and a later merge in the same repository is taken in behind it. Acceptance
tests 53 and 51.
unanswered is said again at the gate's start under its id and listed by `stalled` after 15 minutes; the merge path
of G's gate is checked as §592 says. Acceptance tests 53 and 51.
## The state table, from milestone G's delivery
@@ -271,8 +268,8 @@ Findings 11). How each is kept, read and retained is this design's choice under
- §177 **A revision is made by a merge, and also with no merge.** The indexer makes a catalog graph revision when it
takes a source revision in. The assigner makes an assignment graph revision on each new catalog graph revision, on
the seat `mesh-module-assigner`'s event `assignments-changed` (ADR 0567 §5, ADR 0641), and on the node-engine seat's event
`capabilities-changed`, which a node-engine says when the capabilities it states in its node record change
(ADR 0614 §4), so the assigner follows an event and never reads a node record across the seam (ADR 0596). A
`manifest-reads-changed`, which a node-engine says when the capabilities it states change and when its node returns
(ADR 0614 §4, ADR 0618 §3, §4; §590), so the assigner follows an event and never reads a node record across the seam (ADR 0596). A
placement change is traced like a merge. At its first start with no revision kept, the indexer takes in the head the
gate's source names for every repository by its catch-up path and keeps that as revision 1, and its verb `revision`
answers that it waits until then (lead-47's ruling in G's prepare: a first start has no merge to make a revision).
@@ -72,7 +72,9 @@ One file of milestone G's part of [design 47](../47-delivery-from-commit-to-deli
under ADR 0656 §6: one form of the fact, never `modules` kept beside the catalog graph for these readers, which
would be a second source of the same fact. How it is checked: test 61.
- **The forge module's tools come from mesh-sdk's TypeScript side.** mesh-sdk's TypeScript side has no view
today; milestone G adds there a view follower with its read and ask-again tools, beside the proved caller, a
today; milestone G adds there a view follower with its read and ask-again tools, which adopts and drops a branch's
view as `go/views` does (§532, §614) and follows the branches through the TypeScript form of `meshcatalogclient`
(§614), beside the proved caller, a
verb's `callers` and a seat's publish, so the forge module, reading through the seat's TypeScript contract,
answers `<module>_first_views` and `<module>_first_view_again` as the Go parts do from `go/views`, with no
follower or tool of its own. Its cost: a release of the TypeScript side rebuilds every TypeScript module, which
@@ -223,7 +225,7 @@ One file of milestone G's part of [design 47](../47-delivery-from-commit-to-deli
| §223 The verbs `revision`, `entry` and `difference` on the two seats | each half of the view, and the difference, is served by its one owner through its seat, never read from its state |
| §224 The node-engine reads its want and the catalog half, never the assignment half | the want is the one record a node reads; the assignment half would have each node read every node's placements |
| §225 Composing waits per node, held by the assigner, with no new field in the want: a node's node-engine want first, its other wants once its new node-engine runs | a node away holds only itself (ADR 0603 §7); an old node-engine reads its want as it is, so it is reached with no form it cannot read |
| §226 Capability changes said as the node-engine seat's event `capabilities-changed` | the assigner follows an event instead of reading node records across the seam (ADR 0596) |
| §226 Capability changes said as the node-engine seat's event `manifest-reads-changed` (ADR 0618 §4; §590) | the assigner follows an event instead of reading node records across the seam (ADR 0596) |
| §227 The step events, their fields and their subjects, with the node in the subject of every event a node publishes | every outcome reaches the delivery record as the event of the part that did it (research 085, Findings 7), and a node's account can be granted only its own node's subjects (ADR 0363 §2) |
| §228 A first tier of only the changed acting parts, bounded at 20 minutes, its verdict telling `waiting for a build node` from `did not build` | an ordinary change pays nothing more; a bound turns a build that never ends into a named failure, and the two words tell the operator which act frees it |
| §229 The build seat's ask `run`, published only by the checker's account, its step keys starting with the asker's name | the program is a long job pulled where capacity is, run in the sandbox every check already uses (ADR 0448 §2), and no other part can have a program run or read its verdict |
@@ -284,8 +286,8 @@ say another runs a build (ADR 0363 §2); the gate's events are granted to the ga
| an entry checked | `checked` | `mesh-delivery-checker`, the checker | `mesh.seat.mesh-delivery-checker.event.checked` | the seat's definition in `mesh-delivery-contracts` |
| an entry wanted | `wanted` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.wanted` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) |
| an entry composed, and running healthy | `composed`, `running` | `node-engine`, the node-engine on each node | `mesh.seat.node-engine.event.composed.<node>`, `.running.<node>.<module>`, the latter on a stream kept last per subject with no age limit | the seat's definition in `mesh-contracts` |
| a node's capabilities changed | `capabilities-changed` | `node-engine`, the node-engine on that node | `mesh.seat.node-engine.event.capabilities-changed.<node>` | the same |
| a wait held, and ended | `wait-held` (step, acting part, its version, the entry, since when, its bound), `wait-ended` (the same, and how: ran, released) | each holder: `mesh-wants` for the assigner (placing, wanting and composing), `mesh-grants` for the grants part, the builder's own stream | `mesh.seat.<seat>.event.wait-held`, `.wait-ended`; `mesh.stream.mesh-delivery-builder.event.wait-held`, `.wait-ended` | each seat's definition; the builder's manifest |
| a node's capabilities changed, or its node returned | `manifest-reads-changed` (ADR 0618 §4; §590) | `node-engine`, the node-engine on that node | `mesh.seat.node-engine.event.manifest-reads-changed.<node>` | the same |
| a wait held, and ended | `wait-held` (step, acting part, its version, the entry, since when, its bound), `wait-ended` (the same, and how: ran, released) | each holder: `mesh-wants` for the assigner (placing, wanting and composing), `mesh-grants` for the grants part, the builder's own stream | `mesh.seat.<seat>.event.wait-held`, `.wait-ended`; `mesh.stream.mesh-delivery-builder.event.wait-held.<module>.<fingerprint>`, `.wait-ended.<module>.<fingerprint>` (§672) | each seat's definition; the builder's manifest |
| a store filling, full, and back below its cap | `filling`, `full`, `room` | `mesh-catalog`, `mesh-wants`, `mesh-delivery` | `mesh.seat.<seat>.event.filling`, `.full`, `.room` | each seat's definition |
| a record pins or frees a revision; a candidate's heads replaced; a record ended | `pinned`, `freed`, `heads-replaced`, `record-ended` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.<event>` | the seat's definition in `mesh-delivery-contracts` |
| a milestone's ask asked, withdrawn and answered, until G's delivery | `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.<event>`, the milestone in the event, on the seat's family stream, apart from each delivery's `transition` | the same |
@@ -336,7 +338,7 @@ two records pin stays until both free it; one neither pinned nor current is remo
was stopped is applied after its restart; a candidate's graphs are dropped on `heads-replaced`; a store past 80 % says
`filling` and raises `store-filling`, and a full one says `full` and refuses its next revision naming itself; for a
candidate's heads both `revision` verbs answer, the assigner's half named by its own revision and naming its inputs; a
placement change with no merge, and a `capabilities-changed`, each give a new assignment half on the same catalog half,
placement change with no merge, and a `manifest-reads-changed` that changes what a node states (§590), each give a new assignment half on the same catalog half,
whose difference moves only the module concerned, and a part takes that pair as its view; after its first view a part
acts on a seat event without asking a verb again; a running part does not act on a candidate's view; a pull request
that adds a seat and a module using it is validated by the indexer's candidate pass and checked by the checker with
@@ -157,8 +157,7 @@ record says so in its Why. Round 3 of the review by architectural domain (2026-1
(`mesh.seat.mesh-conditions.event.findings-asked`, carrying `ask`, `at`, `by` and, to ask one, `module`), declared
in the seat's definition, kept 7 days and 16 MiB; an observer hears it through mesh-sdk's writer on a consumer that
starts from the newest ask, so an old ask is never answered again. The keeper asks after its start (§388) and on
`resync` (ADR 0564 §12), which gains an optional `module` and so reads no state; its callers are the operator, the
healers and agent sessions, and each call is written in the hand-act log of the seat `mesh-hand-acts`. An observer
`resync` (ADR 0564 §12), which gains an optional `module` and so reads no state; it is of class `act`, taking its default (§610, §701), and each call is written in the hand-act log of the seat `mesh-hand-acts`. An observer
that states nothing within five minutes of an ask, its machine not away (§389), is the keeper's finding
`findings-unstated`, naming the module and the node, cleared by its next `findings-stated`; its conditions stand
meanwhile. `findings-unstated` is held under ADR 0564 §13's hold while its module is being delivered, and G's
@@ -42,7 +42,7 @@ line says so.*
applies and the manifest validator says per verb ("`<seat>.<verb>` takes the default of `<class>`"): a `read` takes
`invokers`, `persons` and `sessions`; an `act` (a new act that changes what the mesh does: registers, sets a policy,
watches or unwatches a branch, recovers, restores) takes `operator` and `sessions`; a `warrant` (a verb that runs on
the operator's warrant) takes `operator` and carries the approval mark; `accept` is the operator's alone (§394). A
the operator's warrant) takes `operator` and carries the approval mark; `accept` takes `operator` and `sessions` (§394; §610 adds `invokers` to `act`, `warrant` and `accept`). A
verb that names no class, on main or added by G, is a `read`, so every verb open today stays open, and nothing
refuses a verb for naming no class (§456). A verb that names `callers` keeps them, so no verb is
closed by §68 for lack of a list; §68, §127 and §306 point here. Why: ADR 0666 §20 makes the definition the one source
@@ -60,10 +60,10 @@ line says so.*
from the node-engine's code: the definition is the one source (§68). Why: the design's choice (Q-H8). How it is checked: unit (definition reader), each of the eleven names
its callers; the code review finds no callers table in the node-engine.
- §394 **The callers of G's new trusted acts.** (§482) `mesh-catalog`'s `register`, `policy`, `watch` and `unwatch`, and
`mesh-delivery`'s `recovery`, are of class `act`: the operator and sessions; `registrations` is a `read`.
`mesh-delivery`'s `recovery`, are of class `act`, taking its default (§610: the operator, sessions and invokers); `registrations` is a `read`.
`mesh-pipeline-runner`'s `review` is called by sessions, refusing none for authorship in G (§482); its `accept` by
the operator alone, which amends §30's "a person". `operator-channel`'s `ask-again` is a seat verb, its callers the
operator and the healers (§83, §40). Why: the operator, 2026-11-11 02:56 CET on Telegram, "As proposed" (S4, S10,
its class's default (§610), which amends §30's "a person". `operator-channel`'s `ask-again` is a seat verb of class `act`, taking its
default (§610), the healers among its invokers (§83, §40). Why: the operator, 2026-11-11 02:56 CET on Telegram, "As proposed" (S4, S10,
C18, D2, D3, D4). How it is checked: whole (indexer, runner, operator-channel), a call from any other caller is
refused by the bus; unit (definition reader), each verb's class and callers.
- §395 **An accepted kind naming no callers keeps today's grant.** `operator-channel`'s kinds `ask` and `condition`
@@ -108,8 +108,7 @@ line says so.*
(Q13). How it is checked: whole (indexer), `unwatch` of a placed branch refused.
- §403 **A held-back ask.** A held-back ask (§83, design 46 §6) is answered in `asks` with the state `held-back` and the
field `held-since`, and no field `behind`. Why: the design's choice (Q14). How it is checked: the seat's fixtures.
- §404 **The node-engine's events' bodies.** (§520) `capabilities-changed` carries the node record's stated capabilities whole
and `at`; `composed` the module, the fingerprint and the composition's digest; `running` with `stopped` carries
- §404 **The node-engine's events' bodies.** (§520) `manifest-reads-changed` carries what §590 says; `composed` the module, the fingerprint and the composition's digest; `running` with `stopped` carries
`stopped: true` and no fingerprint (§365), and mesh-sdk's `SeatsToPassOn` counts such a module as not running on that
node, its input being §459's. Why: the design's choice (Q17). How it is checked: the seat's fixtures; unit (`SeatsToPassOn`), a row where a
`stopped` holder passes no seat.
@@ -218,11 +217,10 @@ line says so.*
(node-engine), a want naming an entry the half does not hold says `entry-missing`.
- §422 **The node-engine's first-view tools are seat verbs.** The node-engine serves its view's two tools as the seat
`node-engine`'s verbs `first-views` and `first-view-again`, beside its seat-list verbs, since it serves no module
tools: `first-views` a `read`, its callers any person and the healers, and `first-view-again` an `act`, its callers
the operator and the healers, as §204 names them. Why: the design's choice (Q-H4). How it is checked: the seat's
fixtures; whole (node-engine), a call from another caller is refused by the bus.
tools: `first-views` a `read` and `first-view-again` an `act`, each taking its class's default (§610). Why: the design's choice (Q-H4). How it is checked: the seat's
fixtures; whole (node-engine), each verb answers a session's call.
- §423 **A kept-build wait says the existing words.** A have waiting for a kept build (§271) says the existing phase and
words, with no new field. Why: the design's choice (Q-H10). How it is checked: whole (node-engine).
words; its one new field is §629's `wait`. Why: the design's choice (Q-H10). How it is checked: whole (node-engine).
- §424 **Each process's own build, and each bundle's.** (§519) A process says `own build unknown` in its
`<module>_first_views` (§205); each tools bundle's composed environment carries its module's fingerprint. Why: the
design's choice (S12, Q-T7). How it is checked: whole (tool runner), a bundle reads its fingerprint.
@@ -244,7 +242,9 @@ line says so.*
`mesh-config`, `mesh-vault`, `mesh-wireguard`, the tool runner and the bus module, and the branch `main`; in
mesh-catalog, the gate's merge branch, the SDK's repository and the teller's, view's and forge bridge's module
names, the forge module's base branch and the indexer's name and tool, and the build modules' indexer tool; in
`mesh-host`, the node-engine's own module name in its guard, compose and bootstrap; in mesh-sdk, the node-engine's
`mesh-host`, the node-engine's own module name in its guard, compose and bootstrap, and `mesh-wireguard` in
`compose/composed.go` and `cmd/mesh-host/trust.go`, whose removal (with mesh-wireguard's built-in manifest,
`ProvidedManifest` and `ProvidedByMesh`) is design 72's, not G's: G keeps them; in mesh-sdk, the node-engine's
names (§364); in mesh-tools, the assigner's first views by name; in mesh-networking, `mesh-wireguard` and
`mesh-provisioner`. Why: the design's choice (Q-H9, C10, D7). How it is checked: the code review (§7).
- §429 **Lines "until G's delivery".** The new gate builds the forms from G's delivery and the switch (§156, §169)
+7 -1
View File
@@ -6,6 +6,7 @@ milestone: G
updated: 2026-11-11
decisions:
- "ADR 0500"
- "ADR 0673"
- "ADR 0672"
- "ADR 0436"
- "ADR 0670"
@@ -81,7 +82,7 @@ file on 2026-11-10, and is cited as "design 47 G §n". A sentence that holds a d
stayed in the main file, and G builds what it says from G's delivery. A section named in italics is in this part, or,
when it is not here, in the main file.
The part is fifteen files, each read whole, its design lines numbered across them:
The part is eighteen files, each read whole, its design lines numbered across them:
- [G-1-pipelines-registration-and-steps](G-1-pipelines-registration-and-steps.md): pipelines per module: registration, watched branches, steps and policy, what holds a delivery, approval, groups, dependencies, a publish.
- [G-2-pipelines-sizes-checks-and-tools](G-2-pipelines-sizes-checks-and-tools.md): pipelines per module: any size, the pipeline makes the check, who may say what, health, recovery, tools, kept data, how G itself is delivered, the choices under ADR 0666 and ADR 0667.
@@ -98,3 +99,8 @@ The part is fifteen files, each read whole, its design lines numbered across the
- [G-13-review-round-3](G-13-review-round-3.md): the review of §480 to §515, rounds 2 and 3 (§600 on).
- [G-14-recovery-module-and-review](G-14-recovery-module-and-review.md): the recovery module, the operator's answer of 2026-11-11 12:55 CET, and the review of §516 to §553 (§554 to §559).
- [G-15-develop-answers-fifth](G-15-develop-answers-fifth.md): develop's fifth batch, round 3's notes on §516 to §559, the knowledge base's review of the recovery module's record, and the operator's answers of 2026-11-11 about 14:05, 14:15 and 14:30 CET (§560 to §586).
- [G-16-develop-answers-sixth](G-16-develop-answers-sixth.md): develop's sixth batch: the ledger's upkeep calls, the builder's publishes again and its read of a module's builds, one event of a node's capabilities, the object stores' caps, the gate's merge path from G's delivery, and the readers of a module's manifest (§587 to §594).
- [G-17-develop-answers-seventh](G-17-develop-answers-seventh.md): develop's seventh batch: the operator's answers of 2026-11-11 15:14 to 15:42 CET (callers by class, the five parts' findings, the forge and image registry's other entries, the gate's kept data), how every part reads the catalog half per watched branch, and the refreshed build maps' questions (§610 to §676).
- [G-18-fix-round-six-seven](G-18-fix-round-six-seven.md): the fix round of §587 to §676: the approval mark once no verb names its callers, the keeper's snapshots, the recovery module's verbs, the node-engine's kept entries, the gate before a marked verb's running, develop's D-16 to D-49, and a delivery's wait on another machine's network (§677 to §705).
Design 34's milestone [every-event](../34-milestones/every-event.md) and design 46's milestone [7-approval-mark](../46-milestones/7-approval-mark.md) ship in milestone G's release, built in its repositories beside G's lines (lead-47, 2026-11-11 15:30 CET); where one of their lines and a line of G differ, G's later line holds and the other is corrected in place (§624 to §626).