Design 47 takes in round 1 of the review by architectural domain

The reviewers found the amendment left the stores to grow without a bound,
the waits without an owner's tools or events, a rollback that would send nodes
back to old builds, a newer indexer that could never be wanted, events any
account could forge, and the node-engine reading every node's placements. The
design now chooses, each with its reason: a default record retention and caps
with fill conditions, a revision removed when no record pins it and it is not
current, waits held and released by each step's own part with their events,
the build module's wait ending on the agreed toolchain, the old keys kept
written until a named milestone H for a rollback, the running indexer making
the newer indexer's entry, rule changes named by the running checker from
outside the sandbox, and subjects and grants that let each part say only its
own. The review verb leaves milestone G for issue 1782, and the watcher's
definition goes back to main, since it contradicted an approved instruction.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-06 21:18:14 +01:00
parent 8d98d54bd2
commit de7358a2c5
3 changed files with 379 additions and 248 deletions
+2 -7
View File
@@ -23,13 +23,8 @@ without that read on each machine the module is wanted on.
Loop: wait with `mesh_wait` on the event family (its answer only wakes you), then read the state from the verb
the handover names; never report a state you did not read in that round. Report and end at the first of: the
thing reached its end (passed, merged, delivered, accepted, chosen, declined), it failed (quote every failure
line word for word), the bound passed (say what you last read, when, and since when nothing moved), or a call failed
twice (quote it).
**Never end on a quiet five minutes.** Your final report is your only channel to the lead: once you end, nothing
watches the thing. Five minutes with no change while something should be moving is no end: note it, keep waiting,
and put it in your final report (what stood still, from when to when, what you last read). A lead that wants to hear
of stillness sooner gives a shorter bound.
line word for word), five minutes passed with no change while something should be moving (say what you last
read and when), the bound passed, or a call failed twice (quote it).
You only read: no verb that writes, never ssh, never a shell helper. Report in a few lines, with times in UTC as
the tools give them; the lead says them to the operator in local time.
+1 -1
View File
@@ -552,7 +552,7 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
fingerprints, served by the seat `mesh-catalog`'s verb `revision`, and the assignment graph computed from it,
served by the seat `mesh-wants`'s verb `revision`, for a trunk revision or a candidate's heads; a pair, each half
served by its one owner (ADR 0656; the verbs' names, design 47). A part with none for the current revision waits
and says so (ADR 0656).
and says so (ADR 0656). The node-engine reads only the catalog half, beside its want (design 47).
- **delivery record** — the gate's record of one milestone's delivery, a ledger instance it alone writes: the group's
state (heads, verdicts, checks, asks), the trace (milestone, issues, pull requests, commits, the assignment graph
revisions it moved between) and what happened to each entry on each node (ADR 0656). It holds no tiers and nothing
@@ -227,7 +227,7 @@ source revision is derived from the keys: every **want** equals its module's **f
changed by a proposal (ADR 0567; ADR 0446 §3 read as written). Its own composition stands in for every sealed value, so only a node's answer to a proposal can say that
what the node would run is the same. From research 085's milestone it computes the assignment graph from the
catalog graph, the placements and the nodes' capabilities, keeps each revision (ADR 0656) as objects
in its manifest's existing `objects` section (ADR 0619 §2), serves it and a change's graph difference by verbs of
in an `objects` section in the old form (ADR 0619 §2), serves it and a change's graph difference by verbs of
its seat `mesh-wants`, and writes the wants from a trunk revision's assignment graph alone (ADR 0656).
- **The node agent**, the node-engine's own process, follows its node's wants, **composes each wanted module for
its own node** with the composition of `mesh-delivery-sdk` from the manifest by digest, the module's resolved values from
@@ -407,7 +407,8 @@ watches that key and stops only when the head in it moves; a trunk move never en
### The delivery record
*ADR 0656; ADR 0595, ADR 0596, ADR 0597, ADR 0603 §7, ADR 0493 §3, §11, ADR 0633 §4, §5, ADR 0441 §1, ADR 0548 §8.*
*ADR 0656; ADR 0595, ADR 0596, ADR 0597, ADR 0603 §7, ADR 0493 §3, §11, ADR 0633 §4, §5, ADR 0441 §1, ADR 0548 §8,
ADR 0564 §2, ADR 0363 §2.*
From research 085's milestone the gate, holder of the seat `mesh-delivery`, keeps one **delivery record** per
milestone's delivery, a ledger instance (ADR 0595's template `ledger`, ADR 0597) it alone writes (ADR 0656). It
@@ -417,18 +418,25 @@ holds:
- **the trace**: the milestone, its issues, its pull requests, their commits, and the graph revisions the delivery
moved between;
- **what happened to each entry on each node**: for every entry of the graph difference, taken in, built, checked,
composed, wanted, running healthy, or away (ADR 0656).
composed, wanted, running healthy, held (with the hold's reason, so a reader of the record sees why an entry stands
still without asking its holder), failed (with what failed), or away (ADR 0656).
It holds no tiers, and only the gate orders work by it, so it is no second copy of the keys, the objection research
063 had to a stored plan (research 085, Findings 10). Its fields, retention and tools are this design's choices under
ADR 0656.
- **The outcomes arrive as events, through a durable consumer of the gate's own.** The gate's manifest `consumes`
each step event of *What each step says* (below). The consumer resumes from the last event applied; an event
applied twice changes nothing; an event is applied only when it comes from its owner's account; and running
healthy is taken from the node-engine seat's `running` event, never from a have read across the seam (ADR 0596).
- **It ends when every present node runs its entries.** It names the nodes away, each with since when, and ends
without waiting for them, as a node away holds nothing (ADR 0603 §7).
each step event of *What each step says* (below), and `wait-held` and `wait-ended` of each holder. The consumer
resumes from the last event applied; an event applied twice changes nothing; an event is applied only when it comes
from its owner's account; and running healthy is taken from the node-engine seat's `running` event, never from a
have read across the seam (ADR 0596).
- **It ends when every present node runs its entries**, and the gate says `record-ended` on the seat `mesh-delivery`
with the record's milestone and its nodes away. It names the nodes away, each with since when, and ends without
waiting for them, as a node away holds nothing (ADR 0603 §7).
- **A record open too long is a finding.** The watchman writes the finding `record-open` for a record still open 24
hours after its merge (a delivery of the whole mesh takes under an hour, so a day means something stands still) and
clears it on `record-ended`; the condition keeper raises it (ADR 0564 §2). The finding names the entries still held
or failed, read from the record's own events.
- **A node away that wakes adds its outcomes.** When it wakes and catches up by its wants (*A change's graph
difference is its delivery*, below), its outcomes reach the record through the same consumer as any other, and the
record shows the node no longer away.
@@ -440,40 +448,48 @@ ADR 0656.
delivery record like any milestone; nothing is merged outside a released milestone (ADR 0493 §11).
- **It pins the graph revisions it points at.** The gate says, as the seat `mesh-delivery`'s events `pinned` and
`freed`, which catalog and assignment graph revisions a record starts and stops pointing at, and as
`heads-replaced` that a candidate's heads were replaced or its pull request closed; the indexer and the assigner keep
and drop their revisions and a candidate's graphs only on these events, never by reading the gate's state
(ADR 0596; *Two graphs, one source of truth*, below).
- **A record is kept for good unless the gate's manifest sets a retention.** When the retention removes a record,
the gate says `freed` for each revision it pinned.
`heads-replaced` that a candidate's heads were replaced or its pull request closed. Only the gate's account may
publish them (ADR 0363 §2). The indexer and the assigner hear them through a durable consumer each, declared in
their manifests' `consumes`, which resumes from the last event applied after a restart, so a `freed` said while one
of them was down is still applied; neither reads the gate's state (ADR 0596; *Two graphs, one source of truth*,
below).
- **A record is kept for 90 days by default**, the gate's setting `record-retention`. Ninety days covers every trace
the operator has asked of a past delivery so far, which went back weeks, never months; the setting can keep records
longer. When the retention removes a record, the gate says `freed` for each revision it pinned.
- **Its size, its cap and its growth.** One record of 30 modules on four nodes is about 30 KiB, against the bus's
256 KiB value limit (research 085, Findings 8). At an estimated 20 deliveries a day (a guess from the days of
research 085, not a measurement) the ledger grows about 0.6 MiB a day, about 54 MiB at the default retention; its
cap is 128 MiB.
- **The switch carries today's state in, and a rollback reads it back.** At the new gate's start, `merges`, `turns`
and every group in flight become records, and the new gate keeps `merges` and `turns` current beside the records
until a later change removes them, so a gate rolled back reads current state and no approved group is asked again.
and every group in flight become records, and the new gate keeps `merges` and `turns` current beside the records,
so a gate rolled back reads current state and no approved group is asked again; milestone H (*Milestones*) stops
writing them.
- **The forge bridge asks the seat for a group.** It reads a group's members from the seat `mesh-delivery`'s verb
`groups`, never the gate's `merges` state, both to set the group on each merged message and to publish a group's
end again (ADR 0656, which changes ADR 0633 §4 and §5; ADR 0596).
- **Its size.** One record of 30 modules on four nodes is about 30 KiB, against the bus's 256 KiB value limit and a
64 MiB bucket (research 085, Findings 8).
end again (ADR 0656, which changes ADR 0633 §4 and §5; ADR 0596). Its account holds no grant to read `merges`.
- **Its tools**, against the four lines a module's tools answer:
| Line | The gate |
|---|---|
| read everything it holds | `gate_record` (one record whole: the group's state, the trace, each entry's outcome per node, the nodes away), `gate_records` (every record with its fill against the value limit), `gate_consumer` (its durable consumer: the last event applied per family, the events waiting, the age of the oldest) |
| do by hand what it does itself | `gate_record_repair` (ask each part for its outcome again and apply the answers, completing a record missing one) |
| read everything it holds | `gate_record` (one record whole: the group's state, the trace, each entry's outcome per node, the nodes away), `gate_records` (every record with its fill against the value limit, and the ledger's fill against its cap), `gate_consumer` (its durable consumer: the last event applied per family, the events waiting, the age of the oldest) |
| do by hand what it does itself | `gate_record_repair` (for one record, asks each owner for the outcome of each entry missing one through that owner's read verb: the indexer's `take-in` read, the builder's build read, the checker seat's check read, the seat `mesh-wants`'s `entry`, and the node-engine seat's have read of that node; applies the answers as the consumer would) |
| recover without help | `gate_record_end` (end a record left open, a node gone for good or an entry that never runs, with the reason, recorded in the hand-act log of the seat `mesh-hand-acts`) |
| one seat, many tools | the seat `mesh-delivery`'s verbs, with `groups` answering a group's members |
- **How it is checked.** the gate's whole-module test stops and restarts the gate in the middle of a
delivery, and the record is complete with no outcome counted twice; live, one call of `gate_record` traces an
issue of the registry milestone to each node's running build . In the gate's whole-module
test: the events `pinned`, `freed` and `heads-replaced` are said; a group approved before the
switch is not asked again after it, nor after a rollback; a record ends once every present node runs its entries,
naming the nodes away; a node away that wakes adds its outcomes and is no longer shown away; a group's heads
approved and checked again ask the operator nothing again; a small fix's merge gets a record of its own; a record
removed by a set retention frees its revisions; the tools read a record, list the records with their fill, and a
repair completes a record missing an outcome. In the forge bridge's whole-module test, a group's members are read
from `groups` and its end published again from it. Live: the trace also holds after six later merges; a node away
is named with since when and its outcome taken on waking; a record of 30 modules on four nodes stays under
256 KiB; a group checked again at old heads after a main move is not asked of the operator again (issue 1972).
- **How it is checked.** In the gate's whole-module test: the gate stopped and restarted in the middle of a delivery
leaves the record complete with no outcome counted twice; the events `pinned`, `freed`, `heads-replaced` and
`record-ended` are said; a `freed` from an account other than the gate's is refused by the bus; a group approved
before the switch is not asked again after it, nor after a rollback; a record ends once every present node runs its
entries, naming the nodes away; a held and a failed entry are kept with their reason; a record open past 24 hours
raises `record-open`, cleared by `record-ended`; a node away that wakes adds its outcomes and is no longer shown
away; a group's heads approved and checked again ask the operator nothing again; a small fix's merge gets a record
of its own; a record past its retention is removed and frees its revisions; the tools read a record, list the
records with their fill, and a repair completes a record missing an outcome through the owners' read verbs. In the
forge bridge's whole-module test, a group's members are read from `groups` and its end published again from it, and
a read of `merges` by its account is refused. Live: one call of `gate_record` traces an issue of the registry
milestone to each node's running build, also after six later merges; a node away is named with since when and its
outcome taken on waking; a record of 30 modules on four nodes stays under 256 KiB; a group checked again at old
heads after a main move is not asked of the operator again (issue 1972).
## The state table
@@ -512,7 +528,7 @@ module's progress on each node is its want against its have, shown by the view (
*ADR 0656, which supersedes ADR 0645 to ADR 0648; ADR 0595,
ADR 0596, ADR 0603 §2, §6, §7, ADR 0567 §5, ADR 0571 §8, ADR 0614 §4, ADR 0618 §2, ADR 0619 §1 to §3, ADR 0636 §1,
§4, §7, ADR 0639 §7, §8.*
§3, §4, §7, ADR 0639 §7, §8, ADR 0564 §2, ADR 0363 §2.*
From research 085's milestone. One graph answered two questions, which modules exist and which version runs where,
and one module running as several placed versions would have given one entry two shapes and one key two writers; two
@@ -523,7 +539,7 @@ Findings 11). How each is kept, read and retained is this design's choice under
manifests ──► indexer (mesh-catalog) ──► catalog graph revision ──┐
├──► assigner (mesh-wants) ──► assignment graph revision
placements (mesh-module-assigner) ───────────────────────────────┤ │ │
node capabilities (node records) ────────────────────────────────┘ │ └─► wants, from a trunk revision only
node capabilities (node-engine seat) ────────────────────────────┘ │ └─► wants, from a trunk revision only
▼
a change's assignment graph − the current one = its graph difference = its delivery
```
@@ -535,35 +551,60 @@ Findings 11). How each is kept, read and retained is this design's choice under
fingerprint, with the rules the seats declare (*An acting part's new version acts first*, below).
- **The assignment graph is the assigner's, computed and never written by hand.** The assigner, holder of the seats
`mesh-module-assigner` and `mesh-wants`, computes the module versions placed on nodes, with the edges between
versions, from the catalog graph, the placements and the nodes' capabilities, and nothing else writes it (ADR 0656
§1). Each entry is one module version on a node; its edges are provisioning, contribution, and the acting parts'
versions that are to act on it (ADR 0656), whose rules and versions come in with the catalog graph. It is kept, but
it is no stored plan: it has one writer, no part orders work by a stored plan, and any revision is computed again
from the inputs it names and compared with the one kept, by a tool (below), so a kept revision is a checked
versions, from the catalog graph, the placements and the nodes' capabilities, and nothing else writes it
(ADR 0656). Each entry is one module version on a node; its edges are provisioning, contribution, and the acting
parts' versions that are to act on it (ADR 0656), whose rules and versions come in with the catalog graph. It is
kept, but it is no stored plan: it has one writer, no part orders work by a stored plan, and any revision is computed
again from the inputs it names and compared with the one kept, by a tool (below), so a kept revision is a checked
derivation, never a second source of the kind research 063 removed (research 085, Findings 5).
- **Each graph is kept per revision, and each revision names its inputs** (ADR 0656). The indexer keeps a catalog
graph revision for each source revision it takes in; the assigner keeps an assignment graph revision for each change
of its inputs, naming the catalog graph revision, the placements and the capability readings it was computed from.
Both parts are the foundation (ADR 0619 §1), so both keep their revisions as objects declared in their manifests'
existing `objects` sections, each with a cap it declares there, never in the new form (ADR 0619 §2), and neither
waits on the bus module to start; the manifest validator refuses either manifest that declares them in the new form
Both parts are the foundation (ADR 0619 §1), so both keep their revisions as objects declared in an `objects`
section in the old form, each with a cap it declares there, never in the new form (ADR 0619 §2), and neither waits
on the bus module to start; the manifest validator refuses either manifest that declares them in the new form
(ADR 0619 §3). An assignment graph revision is announced by an event of the seat `mesh-wants` and served by its
verbs. This replaces ADR 0603 §2's one key per module with five past values (ADR 0656, which changes ADR 0603 §2);
until research 085's milestone is delivered, those keys and their five values stay as they are.
verbs. This replaces ADR 0603 §2's one key per module with five past values as the graph's form (ADR 0656, which
changes ADR 0603 §2); the keys themselves stay written beside the revisions until milestone H, so that a rolled-back
indexer and assigner read current state (*Rolled back*, below).
- **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 `placement-changed` (ADR 0567 §5), and on a change of the capabilities a
node-engine states in its node record (ADR 0614 §4). A placement change is traced like a merge.
- **A revision is kept while a delivery record pins it.** A revision of either graph, and the input revisions it
names, is kept as long as a delivery record points at it: the indexer and the assigner remove one only on the gate's
event `freed`, never on a timer and never by reading the gate's state (ADR 0596; *The delivery record*, above).
While records are kept for good, the revisions they pin are kept with them. This is the graphs' retention in place
of ADR 0603 §2's five past values, which ADR 0656 leaves here (its Consequences).
the seat `mesh-module-assigner`'s event `placement-changed` (ADR 0567 §5), 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
placement change is traced like a merge.
- **A revision is kept while a record pins it or it is current.** A revision of either graph, and the input revisions
it names, is removed when no delivery record pins it and it is not current: neither trunk's newest revision nor the
newest of an open candidate. Pins are counted per revision, so a revision two records point at stays until both
have said `freed`. The indexer and the assigner remove one only on applying `freed` or on a revision ceasing to be
current, never on a timer and never by reading the gate's state (ADR 0596; *The delivery record*, above). A trace
must read every revision it moved between, and a revision neither pinned nor current is read by nobody.
- **The caps and the growth** (estimates, to be read from the fill tools once the milestone runs). An assignment graph
revision of about 120 modules on four nodes is under the bus's 256 KiB value limit (its unit test), and a catalog
graph revision of about 120 entries about 120 KiB. A record pins the revisions before and after its delivery, and
consecutive records share one, so at an estimated 20 deliveries a day about 20 revisions of each graph a day stay
pinned: about 4 MiB a day of assignment graph and 2.4 MiB of catalog graph, about 360 MiB and 216 MiB at the
records' default retention of 90 days. The caps are 512 MiB for each. Placement-only and capability-only revisions
are pinned by no record and go when they stop being current.
- **A store filling is said before it is full.** The indexer, the assigner and the gate each say their fill against
its cap in their read tools, and the event `filling` of their seat when a store passes 80 % of its cap; the watchman
writes the finding `store-filling` naming the part and the store, the condition keeper raises it (ADR 0564 §2), and
it clears when the store falls below 70 %. A full cap refuses loudly, naming the part and the store: the indexer
keeps no new revision and says the merge pending with the reason `objects full`, the assigner keeps its last
revision and writes no new want, and the gate opens no new record and holds the merge; each says it as the event
`full` of its seat, which the condition keeper raises as urgent. Nothing is dropped to make room: a removal is only
the rule above, and the setting `record-retention` or a larger cap is the operator's.
- **What happened is not in the graph.** An entry says what is to run and what it waits on; whether it was built,
wanted or runs healthy is said by the parts as their events and kept in the delivery record (ADR 0656).
- **The want stays the one record a node reads.** The assigner writes `want.<node>.<module>` from a trunk
revision's assignment graph only; a node-engine reads its want, never the graph, and the have stays what it runs
(ADR 0656, which changes ADR 0571 §8).
revision's assignment graph only; a node-engine reads its want, never the assignment graph, and the have stays what
it runs (ADR 0656, which changes ADR 0571 §8).
- **Rolled back.** A milestone that has to be reverted brings back the indexer and the assigner of before it. They
read ADR 0603 §2's per-module keys and write the wants, both of which the new indexer and assigner keep writing,
current, beside the revisions; the want's form is unchanged. So a reverted indexer and assigner start from current
state, and no want goes back to a fingerprint older than the one each node runs. Writing the old keys is chosen over
converting the revisions back at a rollback because a rollback is the moment nothing new should have to run. The
later change that removes them is milestone H, once milestone G has run for one record retention with no rollback.
### A change's graph difference is its delivery
@@ -576,8 +617,8 @@ Findings 11). How each is kept, read and retained is this design's choice under
`difference`, with the waits and holds read through their owners' tools and the pending revisions through
`indexer_pending`.
- **The preview is a candidate's difference.** Issue 1874's plan preview of a group before its merge is its
candidate's difference, read by one call of `difference`, and needs no code of its own: a candidate's graphs are computed for its check
anyway, so the preview costs nothing more.
candidate's difference, read by one call of `difference`, and needs no code of its own: a candidate's graphs are
computed for its check anyway, so the preview costs nothing more.
- **A changed fingerprint with no difference is refused by name.** When a module's fingerprint in the change
differs from the running one while the difference is empty, the assigner refuses it, naming the module, and the
check fails naming it.
@@ -586,9 +627,11 @@ Findings 11). How each is kept, read and retained is this design's choice under
were replaced or its pull request closed. A candidate costs a computed graph per check.
- **Only a trunk revision writes wants**, whether a merge taken in or a placement or capability change made it; a
candidate's graph writes none (ADR 0656).
- **Each part acts on its own entries.** The builder, the build modules, the grants part and the node-engine each act
when their own entries in the new revision differ from what they run (ADR 0656), reading them through the seat
`mesh-wants`'s verbs and events, never its bus state (ADR 0596, ADR 0656), and naming no other module (ADR 0656).
- **Each part acts on its own entries.** The builder, the build modules and the grants part each act when their own
entries in the new revision differ from what they run (ADR 0656), reading them through the seat `mesh-wants`'s verbs
and events, never its bus state (ADR 0596), and naming no other module (ADR 0656). The node-engine acts on its want
alone, as it does today: the want is the one record a node reads, and handing every node the assignment half would
have each node read every other node's placements.
- **A node asleep catches up when it wakes.** A node away holds nothing and catches up by its wants (ADR 0603 §7);
a want's new form reaches only a node-engine that says it reads it (ADR 0614 §4, ADR 0618 §2).
- **No delivery goes ahead of a change.** A change is delivered as its own difference, its acting parts' new versions
@@ -604,17 +647,24 @@ Findings 11). How each is kept, read and retained is this design's choice under
inputs, the catalog graph revision, the placements and the capability readings. A part's view for the current
revision is the newest assignment half and the catalog half it names. `seat-definitions` stays the seat list's verb
(ADR 0636 §4), and the catalog half carries the same definitions at its revision.
- **Every part takes a first view, then follows the events.** The checker, the builder, the grants part, the
node-engine and the indexer's own later passes take a first view from the two verbs and then follow the two seats'
events, never a list compiled into their build nor another module's bus state (ADR 0656, ADR 0596). Of the view the
assigner reads only the catalog half, never its own output back; its other inputs stay the placements and the
capabilities (ADR 0656). A running part acts only on a trunk revision's view; a candidate's view is read only by
the parts that judge a candidate, inside the check its manifest validator and composer, and the indexer's candidate
pass (ADR 0656, which changes ADR 0636 §4 in that the candidate pass joins them).
- **Every part takes a first view, then follows the events.** The checker, the builder, the grants part and the
indexer's own later passes take a first view from the two verbs and then follow the two seats' events, never a list
compiled into their build nor another module's bus state (ADR 0656, ADR 0596). The node-engine reads only the catalog
half, for the seat definitions it composes with, and its want; it never reads the assignment half (*Each part acts on
its own entries*, above). Of the view the assigner reads only the catalog half, never its own output back; its other
inputs stay the placements and the capabilities (ADR 0656). A running part acts only on a trunk revision's view; a
candidate's view is read only by the parts that judge a candidate, inside the check its manifest validator and
composer, and the indexer's candidate pass (ADR 0656, which changes ADR 0636 §4 in that the candidate pass joins
them).
- **A part with no view says it waits.** It names the revision it has no view for, acts on nothing new, and keeps
running what it runs, as ADR 0636 §1 has a reader of the seat list do; the checker, the builder and the grants part
wait too, and wait for the view, not only the seat list (ADR 0656, which changes ADR 0636 §1). A part restarted
while the indexer is down waits and says so.
wait too, and wait for the view, not only the seat list (ADR 0656, which changes ADR 0636 §1); the node-engine waits
for the catalog half as ADR 0636 §1 already has it wait for the seat list. Each answers `waits` in its runtime tool
(below) with the revision it waits for and since when, and after five minutes without a view writes the finding
`view-waits` naming itself and the revision, as a reader of the seat list does, which the condition keeper raises
(ADR 0564 §2); it clears when the view arrives. Five minutes is the bound the seat list's readers already keep, so an
indexer or assigner down for a restart raises nothing. A part restarted while the indexer or the assigner is down
waits and says so.
- **Each process knows its own build from its start.** The node-engine hands every process it starts the
fingerprint of the build it starts it with, and the process takes its own build from that, never learning it later
(ADR 0656, which changes ADR 0639 §7 from the indexer to every process). *The fingerprint handover*, below, says its
@@ -624,17 +674,21 @@ Findings 11). How each is kept, read and retained is this design's choice under
ADR 0656 leaves how each part is built to this design, under issue 1921; these four needed a shape of their own.
- **An acting rule's fields.** The seat's definition is where ADR 0656 has an acting part declare what it acts on. A seat's definition gains one optional field `acts`, with
two fields of its own: `step`, one of `take-in`, `validate`, `check`, `build`, `compose`, `grant` and `place`; and
`on`, either `every` (every module) or a `language` naming the language of the modules it acts on, matched against
what the manifest says its language is, as the indexer's language map matches today. The definition format's own
schema (design 26) gains the field; the indexer reads it into the catalog graph, the assigner into edges, and the
checking program from the revision view. **Its cost in mesh-sdk:** the runtime in every module reads a definition
only at the seat list's grain (name, scope, verbs, provision) and reads no `acts`; if the runtime's decoder refuses
a field it does not read, it gains a pass-over of unread fields, which is a change of mesh-sdk's runtime and
rebuilds every module once. That rebuild is the same one the fingerprint handover makes (ADR 0656, Consequences), so
the milestone rebuilds every module once in all, and no other module's code changes. Which of the two the runtime's
decoder does today was not read for this amendment (*What is not decided here*).
- **An acting rule's fields.** The seat's definition is where ADR 0656 has an acting part declare what it acts on. A
seat's definition gains one optional field `acts`, with three fields of its own: `step`, one of `take-in`,
`validate`, `check`, `build`, `compose`, `grant` and `place`; `on`, either `every` (every module) or a `language`
naming the language of the modules it acts on, matched against what the manifest says its language is, as the
indexer's language map matches today; and `bound`, how long an entry waits in that step for the acting part's new
version (*An acting part's new version acts first*, below, gives each step's default). The definition format's own
schema (design 26) gains the field, and the format stays 1. The indexer reads it into the catalog graph, the assigner
into edges, and the checking program from the revision view. **Its cost in mesh-sdk:** the seat-definition reader in
mesh-sdk's runtime refuses a definition with a field it does not know or a format other than 1, so a reader built
before the field refuses every definition that carries `acts`, and a refused definition blocks new claims of that
seat only (ADR 0636 §3), never what runs. The runtime therefore learns `acts`, which rebuilds every module once
(ADR 0656, Consequences); that is the same rebuild the fingerprint handover makes, so the milestone rebuilds every
module once in all, and no other module's code changes. A definition carrying `acts` reaches only readers built with
that runtime: the seat definitions that gain it are members of milestone G's group, and every reader is rebuilt in
the same delivery.
- **The fingerprint handover.** The node-engine starts each process, a service or a container alike, with the
environment value `MESH_FINGERPRINT`, the fingerprint of the want it applies; mesh-sdk's runtime reads it once at the
process's start and holds it as the process's own build (ADR 0656). A process started with no value says `own build
@@ -647,11 +701,17 @@ ADR 0656 leaves how each part is built to this design, under issue 1921; these f
builds the named program at them, runs it in the run step (ADR 0448 §2) with the input the ask carries as a file,
and writes the program's output and its exit on the build module's step key named by the ask's id (ADR 0525 §6). It
is an accepted ask of the seat, not a request verb, because it is a long job pulled where there is capacity, never
a call to a named node. The seat definition of `node-build-go` in `mesh-delivery-contracts` names it; the checking
program, the acting parts and the composer a check builds from a change all run through it (ADR 0656, ADR 0644).
a call to a named node. Only the checker's account may publish `run`, and the checker's account reads only the step
keys of its own asks' ids (ADR 0363 §2), so no other part can have a program run or read its verdict. The seat
definition of `node-build-go` in `mesh-delivery-contracts` names it; the checking program, the acting parts and the
composer a check builds from a change all run through it (ADR 0656, ADR 0644). A program is built once per
fingerprint: the build is the builder's build of that fingerprint (ADR 0498 §1), kept like any build and reused by
every later ask naming it, so two checks of changes that leave the checking program unchanged ask one build.
- **The first tier's own bound.** A check's first tier has 20 minutes for all its builds together, inside the check's
one-hour bound of the state table and the checker's step bound (ADR 0639 §5); an acting part not built within it is
one the first tier cannot build, and the check fails naming it (ADR 0656).
one the first tier cannot build, and the check fails naming it (ADR 0656). The verdict tells two cases apart: `waiting
for a build node` when no build node of the language took the ask within the bound, and `did not build` when one
took it and the build failed, so the operator knows whether to free a build node or fix the change.
### Choices this design makes under ADR 0656
@@ -660,26 +720,36 @@ ADR 0656 decides the direction and leaves every choice made in working it out to
| Choice | Why |
|---|---|
| Both graphs kept as objects in the indexer's and the assigner's existing `objects` sections, each with a cap | both parts are the foundation, whose new kept data goes on the old sections so neither waits on the bus module to start (ADR 0619 §1, §2) |
| ADR 0603 §2's keys and five past values stay until the milestone's delivery | what runs keeps reading them until the parts that read revisions are delivered in the same milestone |
| A revision kept while a delivery record pins it, removed only on the gate's `freed` | a trace must read every revision it moved between; a timer or a read of the gate's state would drop one still pointed at, or cross the seam (ADR 0596) |
| Both graphs kept as objects in an `objects` section in the old form, each with a cap of 512 MiB | both parts are the foundation, whose new kept data goes on the old sections so neither waits on the bus module to start (ADR 0619 §1, §2); the caps hold the estimated 90 days with room |
| ADR 0603 §2's keys and the wants kept written beside the revisions until milestone H | a reverted indexer and assigner then read current state and send no node back to an older fingerprint, with nothing to convert at a rollback |
| A revision removed when no record pins it and it is not current, pins counted per revision | a trace must read every revision it moved between, and a revision neither pinned nor current is read by nobody; counting pins keeps a revision two records share |
| The indexer and the assigner hear `pinned`, `freed` and `heads-replaced` through durable consumers | an event said while either was down is applied when it comes back, so no revision leaks and none is dropped early |
| Records kept 90 days by default, the gate's setting `record-retention`, the ledger capped at 128 MiB | the traces asked so far went back weeks; 90 days at an estimated 0.6 MiB a day is about 54 MiB |
| A store passing 80 % of its cap said as `filling` and raised; a full cap refused as `full`, naming the part | the operator hears of a store filling while there is time, and a full store stops work loudly instead of dropping what a trace needs |
| A candidate's graphs kept apart and dropped on `heads-replaced` | a candidate must never write wants or be read by a running part, and its graphs mean nothing once its heads are gone |
| 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 |
| The step events, their fields and their subjects | every outcome the delivery record keeps reaches it as the event of the part that did it, with the revisions and the fingerprint that tie it to the trace (research 085, Findings 7) |
| A first tier of only the changed acting parts, bounded at 20 minutes | an ordinary change pays nothing more; a bound turns a build that never ends into a named failure inside the check's hour |
| The build seat's ask `run` | the program is a long job pulled where capacity is, run in the sandbox every check already uses (ADR 0448 §2) |
| 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 |
| 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) |
| 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) |
| 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 |
| The build seat's ask `run`, published only by the checker's account, its step keys read only by it | 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 |
| A program built once per fingerprint and reused | a check that leaves the checking program unchanged costs no build |
| Every program built from a change run with no network and no bus account | a change not yet merged is not trusted, and the sandbox already keeps the logins outside (ADR 0448 §2, §4) |
| A removed or altered rule of the checking program, and an added acting rule, named in the verdict and the Approve ask | either widens or narrows what judges or acts on every module, so the operator sees it before approving |
| The rule names of the trunk's and the heads' checking programs compared by the running checker, outside the sandbox; a removed, altered, added or widened rule named in the verdict and the Approve ask | the comparison is made from the sources by a part the change did not build, so a program that drops a rule and hides it is still named; a rule widens or narrows what judges or acts on every module, so the operator sees it before approving |
| A rule matching a module with no holder for its step held by name, and its check failed | a rule nobody serves would otherwise pass in silence |
| Each step's wait held by the part that does the step, read by `assigner_waits` and steered by `assigner_release` and `builder_build_again` | the part that does the step already holds its state; the tools are those ADR 0603 §11 has |
| The acting rule's field `acts` with `step` and `on` | the steps and the language match are what the indexer's map and the checker's cases held in code (research 085, Findings 2) |
| Each step's wait held by the part that does the step, with its own read and release tools and the events `wait-held` and `wait-ended` | the part that does the step already holds its state, and the watchman and the record follow every wait by events alone |
| The bound per step in the acting rule, with a default per step | the time a step's new version needs is the acting part's to know, not another module's |
| A build module's wait ends on the agreed toolchain naming the new build module | the agreed toolchain is what a fingerprint takes (ADR 0584 §1), so before it names the new build module no module could be built once by it |
| The running indexer makes a catalog graph revision with the named indexer's entry | the newer indexer can then be wanted and granted, so a revision waiting for it is never stuck behind it |
| The acting rule's field `acts` with `step`, `on` and `bound`, the definition format staying 1 | the steps and the language match are what the indexer's map and the checker's cases held in code (research 085, Findings 2); the reader refuses an unknown field, so the runtime learns it in the one rebuild the milestone makes |
| The fingerprint handed as `MESH_FINGERPRINT` at a process's start | one value every runtime reads before anything else, a service and a container alike |
| The delivery record's consumer, end, node away and wake, switch with `merges` and `turns` kept current, retention and tools | a restart must lose and double nothing; a node asleep must not hold a delivery open; a gate rolled back must find current state and ask no approved group again |
| The delivery record's consumer, held and failed outcomes, end and `record-ended`, the finding `record-open`, node away and wake, switch with `merges` and `turns` kept current, and tools | a restart must lose and double nothing; a reader sees why an entry stands still; a node asleep must not hold a delivery open; a gate rolled back must find current state and ask no approved group again |
| A small fix's own record | a small fix is a milestone of one issue (ADR 0493 §3) |
| An Approve given held while its heads stand (issue 1972) | the operator approved those heads; a check of them asked again changes nothing they approved (ADR 0441 §1, ADR 0548 §8) |
| The milestone events on a subject of their own | a waiter on one member's pull request otherwise wakes on every member's |
| The issue tracker's link kinds to a decision and a research effort, and the knowledge base's link from a decision to its milestone | the trace from research to the running build is then links all the way, with no text match (research 085, Findings 7; ADR 0493 §4) |
| Two view tools in mesh-sdk's runtime bundle beside ADR 0636 §7's, and `node_process_builds` | every part's view and every process's own build is read and steered by a tool, as the seat list already is |
| Two view tools in mesh-sdk's runtime bundle beside ADR 0636 §7's, the finding `view-waits` after five minutes, and `node_process_builds` | every part's view and every process's own build is read and steered by a tool, and a part waiting for the view is raised on the bound the seat list's readers keep |
| Every drop and release tool writes the hand-act log | a hand act that changes what the mesh runs or keeps is recorded with who did it and why, as every hand act is (seat `mesh-hand-acts`) |
Each choice is checked by the tests its section names; the review by architectural domain judges them.
@@ -691,7 +761,9 @@ and the assignment graph revisions; an entry taken in, built, checked, composed,
entry's fingerprint and its node. The gate's delivery record is built from them. The seat's event comes first where
the parts follow it through a seat (ADR 0656); a holder's own event stays under its own subject, declared in its own
manifest's `streams` and published on `mesh.stream.<module>.event.<event>` (ADR 0363 §1, §2). A seat's event is
declared in its definition and its holder's manifest claims the seat.
declared in its definition and its holder's manifest claims the seat. **An event a node publishes carries the node in
its subject**, and the grants part grants each node-engine's account only its own node's subjects, so one node cannot
say another runs a build (ADR 0363 §2); the gate's events are granted to the gate's account alone.
| Step | Event | Owner: the seat, then the module | Subject | Declared in |
|---|---|---|---|---|
@@ -702,54 +774,63 @@ declared in its definition and its holder's manifest claims the seat.
| an entry built | `build.ended` | the builder's own | `mesh.stream.mesh-delivery-builder.event.build.ended` | the builder's manifest |
| 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-placement-contracts` |
| an entry composed, and running healthy | `composed`, `running` | `node-engine`, the node-engine on each node | `mesh.seat.node-engine.event.composed`, `.running` | the seat's definition in `mesh-contracts` |
| a record pins or frees a revision; a candidate's heads replaced | `pinned`, `freed`, `heads-replaced` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.<event>` | the seat's definition in `mesh-delivery-contracts` |
| 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>` | 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`, `wait-ended` (step, acting part, its version, the entry, since when; for an end, how: ran, released) | each holder: `mesh-wants` for the assigner, `mesh-grants` for the grants part, `node-engine` for the node-engine (with `.<node>`), 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 store filling, or full | `filling`, `full` | `mesh-catalog`, `mesh-wants`, `mesh-delivery` | `mesh.seat.<seat>.event.filling`, `.full` | 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 | `milestone.asked`, `milestone.withdrawn`, `milestone.answered` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.milestone.<milestone>.<event>`, apart from each delivery's `transition` | the same |
The events, their fields, their subjects and which are a seat's are this design's choices: a seat's event where
other parts follow the step through the seat, a holder's own event where only the delivery record hears it, so that
nothing reads another module's bus state (ADR 0596). How it is checked: each seat
definition's conformance fixtures name the subject of every event it gains, and a case of the holder's whole-module
test matches an event it publishes against them, as for every seat (design 26); the gate's whole-module test hears
each row through its durable consumer, with exactly the fields this section gives it.
nothing reads another module's bus state (ADR 0596). The builder's waits go on its own stream because the builder
holds no seat of its own; the watchman and the gate follow them there. How it is checked: each seat definition's
conformance fixtures name the subject of every event it gains, and a case of the holder's whole-module test matches an
event it publishes against them, as for every seat (design 26); the gate's whole-module test hears each row through
its durable consumer, with exactly the fields this section gives it; a `running` event published by one node's account
on another node's subject is refused by the bus.
### Their tools
| Line | The assigner | The indexer |
|---|---|---|
| read everything it holds | `assigner_revision` (one assignment graph revision whole, with the revisions of its inputs), `assigner_entry` (one entry, with the seat rule that gave each edge), `assigner_difference` (a change's difference), `assigner_waits` (*Order without a person*), `assigner_candidate_graphs` (every candidate's graphs: its heads, its revisions, since when) | `indexer_catalog_revision` (one catalog graph revision), `indexer_candidate_graphs` (every candidate's catalog half) |
| do by hand what it does itself | `assigner_recompute` (compute a revision again from its named inputs and compare it with the one kept) | — |
| recover without help | `assigner_drop_candidate_graph` (drop a candidate's graphs whose gate event never came) | `indexer_drop_candidate_graph` (the same for its half) |
| read everything it holds | `assigner_revision` (one assignment graph revision whole, with the revisions of its inputs), `assigner_revisions` (every kept revision with what pins it, and the store's fill against its cap), `assigner_entry` (one entry, with the seat rule that gave each edge), `assigner_difference` (a change's difference), `assigner_waits` (*Order without a person*), `assigner_candidate_graphs` (every candidate's graphs: its heads, its revisions, since when), `assigner_consumer` (its consumer of the gate's events: the last applied, the waiting) | `indexer_catalog_revision` (one catalog graph revision), `indexer_catalog_revisions` (every kept revision with what pins it, and the store's fill against its cap), `indexer_candidate_graphs` (every candidate's catalog half), `indexer_consumer` (the same as the assigner's) |
| do by hand what it does itself | `assigner_recompute` (compute a revision again from its named inputs and compare it with the one kept) | `indexer_recompute` (compute a catalog graph revision again from its source revision and compare it with the one kept) |
| recover without help | `assigner_drop_candidate_graph` (drop a candidate's graphs whose gate event never came), `assigner_release` (*Order without a person*) | `indexer_drop_candidate_graph` (the same for its half) |
| one seat, many tools | `mesh-wants`: `revision`, `entry`, `difference` | `mesh-catalog`: `revision`, beside `seat-definitions` |
Every drop and release tool writes its act, who did it and why, in the hand-act log of the seat `mesh-hand-acts`.
Beside ADR 0636 §7's two tools in mesh-sdk's runtime bundle, which read a reader's seat list and make it read the
list again, the bundle gains two for the view: one reads which view revision a part holds and what it waits for, the
other, granted as ADR 0636 §7's second is, makes it read the view again now. A tool of the node-engine,
`node_process_builds`, reads the build each process was started with. The tools answer the four lines every module's
tools answer (design 26), so that every state these parts keep is read and every act they do is steered by a tool.
list again, the bundle gains two for the view: `waits`, which reads which view revision a part holds and what it
waits for and since when, and one, granted as ADR 0636 §7's second is, that makes it read the view again now. A tool
of the node-engine, `node_process_builds`, reads the build each process was started with. The tools answer the four
lines every module's tools answer (design 26), so that every state these parts keep is read and every act they do is
steered by a tool.
**How it is checked.** a revision of the assignment graph computed again from its named inputs
equals the one kept (unit, assigner); a fixture change with a new fingerprint and an
empty difference is refused, naming its module (unit, assigner), and a group's preview before its merge is its
difference (live) ; 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 the same definitions (whole), and a search of the
readers' code finds no compiled seat list (live) ; a part started while its node's want
differs from what runs reports the build it was started with (whole, node-engine). Unit
(assigner): one revision of about 120 modules on four nodes stays under the bus's value limit, and of
the view the assigner reads only the catalog half; unit (indexer), a catalog graph revision holds one entry per module
of its source revision. Whole (indexer): a source revision taken in gives one catalog graph revision kept as objects
in the old `objects` section within the cap its manifest declares; the manifest validator refuses the indexer's or
the assigner's manifest that declares them in the new form. Whole (indexer, assigner): a revision a delivery record
pins is not removed, and one is removed on the gate's `freed`; a candidate's graphs are dropped on `heads-replaced`;
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 gives a new assignment half on the same catalog half, whose difference moves
only the module placed, 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; with the indexer stopped, a waiting
part keeps running what it runs, and a part asking the view for a new revision says it waits, naming it, and acts once
the indexer is back. Whole (assigner): a pull request's graph leaves every node's want unchanged; a placement-only
revision moves the want of the module it places; the builder, the grants part and the node-engine read their entries
only through the seat `mesh-wants`'s verbs and events; the tools of the table answer, and both view tools answer.
Live: a search of the readers' code finds no read of another module's bus state; an old node-engine that wakes takes
its new build in a form it reads.
**How it is checked.** Unit (assigner): a revision of the assignment graph computed again from its named inputs
equals the one kept; a fixture change with a new fingerprint and an empty difference is refused, naming its module;
one revision of about 120 modules on four nodes stays under the bus's value limit; of the view the assigner reads only
the catalog half. Unit (indexer): a catalog graph revision holds one entry per module of its source revision. Whole
(indexer): a source revision taken in gives one catalog graph revision kept as objects in an `objects` section in the
old form within the cap its manifest declares; the manifest validator refuses the indexer's or the assigner's manifest
that declares them in the new form. Whole (indexer, assigner): a revision a delivery record pins is not removed; one
two records pin stays until both free it; one neither pinned nor current is removed; a `freed` said while the part
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,
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
the same definitions; the tools of the table answer and the drop tools write the hand-act log. Whole (assigner): a
pull request's graph leaves every node's want unchanged; a placement-only revision moves the want of the module it
places; the builder and the grants part read their entries only through the seat `mesh-wants`'s verbs and events, and
the node-engine reads its want and the catalog half and never the assignment half. Whole (node-engine): a part started
while its node's want differs from what runs reports the build it was started with. Live: a search of the readers'
code finds none of the reads the revision view replaces, each by name: the indexer's read of the checker's previews,
the checker's watch of the indexer's `candidates.<try id>`, the forge bridge's read of the gate's `merges`, the
checker's validator request with no seat definitions, and a seat list compiled into the node-engine, the checker, the
builder or the grants part; an old node-engine that wakes takes its new build in a form it reads.
## Order without a person
@@ -777,7 +858,7 @@ per module and node from the keys:
### An acting part's new version acts first, a wait with a bound
*ADR 0656, which supersedes ADR 0646; ADR 0603 §1 to §4, §8, §11, ADR 0614 §1, §3, §6, ADR 0633
§1, ADR 0539 §7, ADR 0584 §1.*
§1, ADR 0539 §7, ADR 0584 §1, §2, ADR 0564 §2.*
From research 085's milestone. A seat's definition may carry a rule, the step its holder acts in and the modules it
acts on, and a holder of such a seat is an **acting part** (ADR 0656): the indexer taking in and validating, the
@@ -789,20 +870,26 @@ names go, since every new acting part needed code changes elsewhere (research 08
- **The rule becomes edges between versions.** In the assignment graph the assigner gives an edge from the acting
part's version to each version its rule names, and no part names another module in its code to order by (ADR 0656).
- **Within a revision the new version acts first, a wait with a bound.** The entries an acting part acts on wait for
its new version in its own step, with a bound (ADR 0656), the part's own as ADR 0603 §8 has every wait. For a build
module the entry waits until the agreed toolchain (ADR 0584 §1) names the new build module, so the module's
fingerprint names the new recipe and it is built once, by the new one: a module changed with its build module is no
longer built with the running recipe and again later, and a build module's edge, a trigger no want waited on,
becomes this wait (ADR 0656, which changes ADR 0603 §1, §3 and ADR 0614 §1, §6). ADR 0603 §3's fingerprint naming
the agreed toolchain stands.
- **A failed or late new version holds its modules, and a condition names it.** When the new version fails, or does
not run within its bound, the entries waiting for it stay held, the modules stay on their running builds, and the
watchman raises a condition naming the acting part; an entry moves on only once the new version runs, or a tool
releases it (ADR 0656: its new versions act first within a bound), and the condition clears when the wait ends. The
operator sees each stuck acting part as one condition, and `assigner_release` steers it. Going on with the running
version instead would build or check a change with a part it replaced, the cause of the failures of research 085,
Findings 4; stopping the whole delivery would hold modules the failed part does not act on (the operator, 2026-11-06
17:0x).
its new version in its own step, with the bound its rule's `bound` gives (ADR 0656), the part's own as ADR 0603 §8
has every wait. When a rule names none, the step's default holds: 60 minutes for building (a build module's own
build and its spread to a majority of build nodes), 30 for composing (a node-engine's apply on every node it runs
on), 10 for granting and placing (a write of one part), and the first tier's 20 for checking. A module changed with
its build module is no longer built with the running recipe and again later, and a build module's edge, a trigger no
want waited on, becomes this wait (ADR 0656, which changes ADR 0603 §1, §3 and ADR 0614 §1, §6).
- **A build module's wait ends on the agreed toolchain.** For building, an entry waits until the agreed toolchain of
its language (ADR 0584 §1) names the new build module, never merely until the new build module runs on one node: a
fingerprint names the agreed toolchain (ADR 0603 §3), so only then is the module built once, by the new one. A build
node still on the old build module builds nothing of that language until it reports the agreed one (ADR 0584 §2).
Every other step's wait ends when the acting part's new version runs healthy, as the node-engine seat's `running`
event says.
- **A failed or late new version holds its modules, and a condition names it.** When the new version fails, or its
wait passes its bound, the entries waiting for it stay held, the modules stay on their running builds, and the
holder says `wait-held` past its bound; the watchman writes the finding `acting-part-late` naming the acting part,
its step and since when, and the condition keeper raises it (ADR 0564 §2). An entry moves on only once the new
version runs, or its holder's release tool releases it; either way the holder says `wait-ended`, and the finding and
the condition clear. The operator sees each stuck acting part as one condition. Going on with the running version
instead would build or check a change with a part it replaced, the cause of the failures of research 085, Findings
4; stopping the whole delivery would hold modules the failed part does not act on (the operator, 2026-11-06 17:0x).
- **A wait holds only within its step, and skips the holder's own inputs** (ADR 0656). An entry waits only for its
own step's holders, so two acting parts that act on each other in different steps (the indexer taking in the
checker, the checker checking the indexer) form no cycle. Within a step a holder's own change is its first act, and
@@ -812,35 +899,50 @@ names go, since every new acting part needed code changes elsewhere (research 08
- **The check refuses a cycle among the waits within one step** (ADR 0656, which changes ADR 0603 §2's refusal of a
cycle), counted beside the edges ADR 0614 §3 and §6 list; a cycle through an acting part's edges across two steps, or
through a holder's own inputs, is none.
- **The indexer's own steps have no wait.** Taking in and validating stay as *Taken in by the indexer it names* has
them: a revision is taken in only by the indexer it names, which judges its own change at its start, and no build
waits on which indexer judged it (ADR 0603 §4, as ADR 0633 §1 changes it).
- **The indexer's own steps have no wait, and a newer indexer is reached.** Taking in and validating stay as *Taken in
by the indexer it names* has them: a revision is taken in only by the indexer it names, which judges its own change
at its start, and no build waits on which indexer judged it (ADR 0603 §4, as ADR 0633 §1 changes it). So that the
named indexer can start at all, the running indexer makes, for a merged revision naming a newer indexer, a catalog
graph revision holding that indexer's own entry, read from its manifest at the merge, and nothing else of the
revision; the assigner wants it and the grants part grants it like any module, and once it runs it takes the revision
in whole. Without that, a revision waiting for its indexer would wait for good.
- **A rule with no holder is no silent pass.** An entry a rule matches with no holder for its step is held by the
assigner, naming the missing holder as the hold's reason, and the check fails naming it.
- **An acting rule grants nothing.** A holder's bus grants come from the grants part as today (ADR 0539 §7). An
acting rule changes what a part acts on across every module it matches, so the operator's Approve ask of a change
names any acting rule it adds.
- **Each step's wait is its own part's**, so that each wait has one owner that already holds that step's state. Building: the builder's
build-key wait; placing and wanting: the assigner's hold; granting: the grants part's; composing: the node-engine's;
checking: the check's first tier (*The check on the change's own acting parts*, below). A wait ends when the acting
part's new version runs healthy, as the node-engine seat's `running` event says.
- **Read and steered by the parts' own tools.** The assigner's `assigner_waits` reads each wait: its entry, its step,
the seat rule that gave it, the acting part's version it waits for and since when; `assigner_release` and
`builder_build_again` steer them, as ADR 0603 §11 has them.
- **How it is checked.** a fixture seat declaring a rule, with a fixture holder, gets its edges
with no code change elsewhere (whole, assigner), and a search of the delivery parts' code finds no other module's
name (live) ; a fixture group changing the shared contracts and the build module that
builds them is delivered, the contracts built by the running build module first and the rest by the new one (whole,
assigner and builder), and a cycle among waits within one step is refused (unit, graph) ; a fixture build module whose new build fails, and one that does not run within its bound, each leave
their modules on their running builds and raise the condition naming it, which clears when the wait ends (whole,
assigner and builder). Whole (assigner, builder): a module with no
holder for its step is held by name, and the check of a change bringing it fails naming the missing holder;
`assigner_release` moves a held entry on; a group changing the indexer and a module is taken in by the new indexer
with no build waiting on it; each step's wait ends on the acting part's new version running healthy as the
node-engine reports it, and `assigner_waits` reads each wait with its step, seat rule, version and since when.
Unit (graph): an acting part acting on itself, two acting parts acting on each other in different steps, and a
cycle through a build module's edge each pass the cycle check, each new version first in its own step. Unit
(assigner): the Approve ask of a change that adds an acting rule names the rule.
names any acting rule it adds, and any it widens: a `step`, an `on` or a longer `bound` that now matches more
modules or holds them longer is named like an added rule.
- **Each step's wait is its own part's, read and released by its own tools.** The part that does a step holds its
wait, so each wait has one owner that already holds that step's state, and says `wait-held` and `wait-ended` (*What
each step says*) with the step, the acting part, its version, the entry and since when:
| Step | Holder | Reads its waits | Releases one |
|---|---|---|---|
| building | the builder, on its build key | `builder_waits` | `builder_release` (build the entry with the running build module), beside `builder_build_again` (ADR 0603 §11) |
| placing and wanting | the assigner, on its hold | `assigner_waits` | `assigner_release` (ADR 0603 §11) |
| granting | the grants part | `grants_waits` | `grants_release` |
| composing | the node-engine on each node: the assigner names in a want the node-engine version that want waits for, and the node-engine applies it only once it runs that version, keeping what it runs | `node_waits` | `node_release` |
| checking | the check's first tier | `checker_first_tier` | its ask-again (*The check on the change's own acting parts*) |
Each release writes the hand-act log of the seat `mesh-hand-acts`, with who released and why.
- **How it is checked.** Whole (assigner): a fixture seat declaring a rule, with a fixture holder, gets its edges with
no code change elsewhere. Whole (assigner, builder, three build nodes): a group changing the shared contracts and the
build module that builds them, the new build module updated on the first build node first, builds no module with the
new build module while one node of three runs it, builds the contracts with the running build module, and once the
second node reports it and the agreed toolchain names it, builds each other module once, by the new one; the third
node builds nothing of the language until it reports the agreed version. Whole (assigner, builder, grants part,
node-engine): a fixture acting part whose new version fails, and one whose wait passes its bound, each leave their
modules on their running builds, say `wait-held`, and raise `acting-part-late` naming it; each holder's release tool
moves a held entry on, says `wait-ended` and writes the hand-act log, and the condition clears; a wait ends on its
own when the new version runs; each holder's read tool lists its waits with step, acting part, version and since
when; a rule's `bound` overrides the step's default. Whole (indexer, assigner): a merged revision naming a newer
indexer gives a catalog graph revision holding that indexer's entry; the new indexer is wanted and granted, starts,
and takes the revision in, with no build waiting on which indexer judged it. Whole (assigner): a module with no
holder for its step is held by name, and the check of a change bringing it fails naming the missing holder. Unit
(graph): an acting part acting on itself, two acting parts acting on each other in different steps, and a cycle
through a build module's edge each pass the cycle check, each new version first in its own step; a cycle among
waits within one step is refused. Unit (assigner): the Approve ask of a change that adds an acting rule, and of one
that widens one, names the rule. Live: a search of the delivery parts' code finds no other module's name.
The planner's edges (*built by*, *SDK before its users*, *verb before caller*, *version skew*) and its one
default, the node-engine before the controller, ordered the tiers of a plan until release 6 (ADR 0419 §3 to
@@ -1039,22 +1141,25 @@ From its delivery, a check is this:
### The check on the change's own acting parts
*ADR 0656, which supersedes ADR 0648, ADR 0644; ADR 0448 §2, §4, ADR 0391 §1, ADR 0603 §10, ADR 0614 §7, ADR 0633
§6, ADR 0636 §4, ADR 0639 §5.*
§6, ADR 0636 §4, ADR 0639 §5, ADR 0498 §1.*
From research 085's milestone. ADR 0603 §10 and ADR 0614 §7 let a check pass while the part that would build or
check the change never ran: 7 of 24 failures since 2026-11-01 (research 085, Findings 4). The acting parts a change alters,
and the checking program, the rules every check applies, are built from the change's heads and check it; the running
versions never check in their place, and a check fails, naming the part, when one cannot be built or run (ADR 0656).
check the change never ran: 7 of 24 failures since 2026-11-01 (research 085, Findings 4). The acting parts a change
alters, and the checking program, the rules every check applies, are built from the change's heads and check it; the
running versions never check in their place, and a check fails, naming the part, when one cannot be built or run
(ADR 0656).
- **Two tiers on the change's assignment graph.** The first tier builds, at the change's heads, only the acting parts
whose fingerprint in the change's assignment graph differs from the running one and which are to act on a module
the check acts on; a change to an ordinary module asks no first-tier build, since building every acting part for
every check would make an ordinary change pay for builds that change nothing. The
second tier runs the check's levels with those programs and with the revision view of the change's heads
(ADR 0656). This keeps ADR 0603 §10's building of changed build modules first and widens it to every acting part.
every check would make an ordinary change pay for builds that change nothing. The second tier runs the check's
levels with those programs and with the revision view of the change's heads (ADR 0656). This keeps ADR 0603 §10's
building of changed build modules first and widens it to every acting part.
- **The judging rules are a checking program built from the change.** Every check builds its checking program from
the change's heads and runs it with the revision view as its input; when it cannot be built or run, the check
fails naming it, and the running checker's own rules never judge in its place (ADR 0656).
fails naming it, and the running checker's own rules never judge in its place (ADR 0656). It is built once per
fingerprint and reused (ADR 0498 §1): checks whose heads leave the checking program unchanged ask one build between
them.
- **Every program built from a change runs only in the build seat's sandbox.** The acting parts the first tier builds
and the checking program run in the run step, with no network (ADR 0448 §2) and no bus account, the logins staying
outside (ADR 0448 §4), through the seat `node-build-go`'s ask `run` (*What ADR 0656 leaves to this design*), as
@@ -1065,9 +1170,12 @@ versions never check in their place, and a check fails, naming the part, when on
manifest (ADR 0656, which changes ADR 0603 §10 and ADR 0614 §7), and ADR 0614 §7's reason, a changed build module
invoking a verb no running holder serves, is one reason the first tier cannot run an acting part: the check fails
naming the build module and the verb. An acting part not built within the first tier's bound of 20 minutes is one
it cannot build.
- **A change that alters or removes a rule of the checking program is named**, in its verdict and in the operator's
Approve ask.
it cannot build, and the verdict says `waiting for a build node` or `did not build`.
- **A change to a rule of the checking program is named, by a part it did not build.** Each checking program
declares its rules by name in its source. The running checker, outside the sandbox, reads the rule names from the
trunk's sources and from the heads' and compares them; every rule removed, added or altered (its source changed) is
named in the verdict and in the operator's Approve ask. The program built from the change is never asked which rules
it has, so a program that drops a rule and hides it is still named.
- **ADR 0633 §6 stays, beside the change's own validator.** Whether the running parts can take the change in is
still checked with the trunk's manifest validator, built from the trunk (ADR 0633 §6); the change's own validator,
built in the first tier, judges the change's manifests besides (ADR 0656). A change fails if either refuses it.
@@ -1077,23 +1185,27 @@ versions never check in their place, and a check fails, naming the part, when on
the members' heads and hands it to the manifest validator and the composer (ADR 0644, ADR 0636 §4 with its insight
of 2026-11-06).
- **Its tools.** The checker's read of a check names the checking program's build that judged it, its fingerprint and
the heads it was built from; `checker_first_tier` lists a check's first-tier builds, each with its acting part, its
fingerprint and its outcome, and asks one of them again. Asking a whole check again stays the checker's existing
ask-again.
- **What it costs.** A check of a change to an acting part costs one build per changed acting part, within the checker's step bound (ADR 0639 §5); an ordinary change costs nothing more; a group's
check costs one build of its checking program.
- **How it is checked.** A fixture change whose acting part does not build, and one whose checking
program does not run, each fail their check, naming the part (whole, checker). In the checker's
whole-module integration test: a checking program that builds but does not run fails
naming it; a change that breaks a rule of the checking program fails its own check; a change that removes a rule
passes, and its verdict and the ask name the removal; a fixture checking program that tries to reach the bus or
the network fails; a fixture build module invoking a verb no running holder serves fails naming it and the verb; a
change to an ordinary module asks no first-tier build; a group that changes the assigner's placing is checked with
its own assigner; a fixture indexer change whose manifest the trunk's validator refuses fails its check, even when
its own validator passes it; a fixture group whose heads add a seat has that seat in the list both the validator and
the composer receive; the check's read names the checking program's build and heads; `checker_first_tier` lists a
check's first-tier builds and asks one again; the changed checker asks one check of a fixture group end to end.
Live: the registry group passes its one check with its own checker, validator and composer.
the heads it was built from, and the rules named changed; `checker_first_tier` lists a check's first-tier builds,
each with its acting part, its fingerprint and its outcome (`built`, `waiting for a build node`, `did not build`),
and asks one of them again. Asking a whole check again stays the checker's existing ask-again.
- **What it costs.** A check of a change to an acting part costs one build per changed acting part, within the
checker's step bound (ADR 0639 §5); an ordinary change costs nothing more; a checking program costs one build per
fingerprint, shared by every check that uses it.
- **How it is checked.** In the checker's whole-module integration test: a fixture change whose acting part does not
build, and one whose checking program does not run, each fail their check, naming the part; a checking program that
builds but does not run fails naming it; a first-tier ask no build node takes within the bound says `waiting for a
build node`, and one that fails says `did not build`; a change that breaks a rule of the checking program fails its
own check; a change that removes a rule passes, and its verdict and the ask name the removal, also when the
program built from the change hides it; a fixture checking program that tries to reach the bus or the network
fails; a `run` ask published from an account other than the checker's is refused by the bus, and the checker's
account cannot read another account's step key; a fixture build module invoking a verb no running holder serves
fails naming it and the verb; a change to an ordinary module asks no first-tier build; two ordinary checks with the
same checking program ask one build of it; a group that changes the assigner's placing is checked with its own
assigner; a fixture indexer change whose manifest the trunk's validator refuses fails its check, even when its own
validator passes it; a fixture group whose heads add a seat has that seat in the list both the validator and the
composer receive; the check's read names the checking program's build, heads and changed rules; `checker_first_tier`
lists a check's first-tier builds and asks one again; the changed checker asks one check of a fixture group end to
end. Live: the registry group passes its one check with its own checker, validator and composer.
### A try: a delivery group's check at exact commits
@@ -1651,9 +1763,9 @@ assignments bucket is kept equal to it by the holder until ADR 0547's third mile
| placement candidate | while a proposal is open: one node's placement as the proposal lays it, which that node composes in shadow and answers | the assigner | the seat's state `candidates` |
| assignment | until ADR 0547's third milestone: the module and node, pinned or not, ended or not, written by `assign` and `unassign` after an approved placement change, so that it equals the placement (ADR 0567) | the controller, called by the holder | `mesh-controller_assignments` |
| rollout | read-only from release 6: each plan of releases 1 to 5, its tiers, the builds it carried and the assignments it changed | nobody from release 6 | `mesh-controller_rollouts` |
| catalog graph revision | from research 085's milestone: every module as its manifest declares it at one source revision, with the seats' acting rules (ADR 0656), kept while a delivery record pins it | the indexer | objects in its manifest's existing `objects` section (ADR 0619 §2) |
| assignment graph revision | from research 085's milestone: every module version on a node at one revision, its edges, and the revisions of its inputs (ADR 0656), kept while a delivery record pins it | the assigner | objects in its manifest's existing `objects` section (ADR 0619 §2), served by the seat `mesh-wants` |
| delivery record | from research 085's milestone: one milestone's delivery, its group's state, its trace and what happened to each entry on each node (ADR 0656), kept for good unless the gate's manifest sets a retention | the gate | a ledger instance of the gate's (ADR 0595, ADR 0597) |
| catalog graph revision | from research 085's milestone: every module as its manifest declares it at one source revision, with the seats' acting rules (ADR 0656), kept while a delivery record pins it or it is current | the indexer | objects in an `objects` section in the old form (ADR 0619 §2), capped at 512 MiB |
| assignment graph revision | from research 085's milestone: every module version on a node at one revision, its edges, and the revisions of its inputs (ADR 0656), kept while a delivery record pins it or it is current | the assigner | objects in an `objects` section in the old form (ADR 0619 §2), capped at 512 MiB, served by the seat `mesh-wants` |
| delivery record | from research 085's milestone: one milestone's delivery, its group's state, its trace and what happened to each entry on each node (ADR 0656), kept 90 days by default (the gate's setting `record-retention`) | the gate | a ledger instance of the gate's (ADR 0595, ADR 0597) |
Beside the chain, on the bus too: deliveries and groups (the gate's `deliveries` and `groups`, ADR 0239 §37), one
key per open pull request (the gate), and the grants part's state (`user-list`, the memberships it last
@@ -1719,25 +1831,26 @@ From research 085's milestone the seat gains:
| Verb or event | Answers or does |
|---|---|
| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656) |
| `review` | records a reviewer's verdict at the full head it judged, as ADR 0493 §5 has it; once it runs, the line in a pull request's description is read no more (ADR 0496 §3, issue 1782). Whether the reviewer is taken from the caller's bus account and a review by the head's author refused is another question, waiting on issue 1782 |
| milestone events | the one Approve ask per milestone (ADR 0493 §9 as ADR 0496 §2 changes it, issue 1783), held until it is answered, or withdrawn by a new head and asked again (ADR 0441 §1); said on a subject of their own, while each delivery's transitions stay as they are, so a waiter on one member's pull request wakes only on that member's `transition` |
| `pinned`, `freed`, `heads-replaced` | the events by which the indexer and the assigner keep or drop graph revisions (*The delivery record*) |
| `milestone.asked`, `milestone.withdrawn`, `milestone.answered` | the one Approve ask per milestone (ADR 0493 §9 as ADR 0496 §2 changes it, issue 1783), held until it is answered, or withdrawn by a new head and asked again (ADR 0441 §1); said on `mesh.seat.mesh-delivery.event.milestone.<milestone>.<event>`, apart from each delivery's `transition`, which stays as it is, so a waiter on one member's pull request wakes only on that member's `transition` |
| `pinned`, `freed`, `heads-replaced` | the events by which the indexer and the assigner keep or drop graph revisions, said only from the gate's account (*The delivery record*) |
| `record-ended`, `filling`, `full` | a delivery record ended; the ledger past 80 % of its cap; the ledger full (*The delivery record*) |
**The seats `mesh-catalog` and `mesh-wants`** gain, from research 085's milestone, `revision` on each (the two halves
of the revision view, ADR 0656), and `entry` and `difference` on `mesh-wants` (ADR 0656), with the events of *What
each step says*.
**Links of the trace.** The review is recorded by the verb `review` (ADR 0493 §5), and the trace's other links are
this design's choices under ADR 0656, so that one call traces research to the running build
**Links of the trace.** A review stays the line in a pull request's description, as ADR 0496 §3 has it, until issue
1782 builds the review verb and settles who may review and its grant, outside milestone G. The trace's links are this
design's choices under ADR 0656, so that one call traces research to the running build
with no text match (research 085, Findings 7). The issue tracker gains link kinds from an issue to the decision and to the research effort
it carries, made by a tool call and never by text, as ADR 0493 §4 has every link made; the knowledge base gains a
typed link from a decision to the milestone of the issue tracker that builds it, made by a tool call, which joins the
link kinds of its link-fits rule (design 55), beside the `became` link a research effort gets at its graduation
(playbook 02). How it is checked: whole (gate), the verdict is read from the verb, not the description; one ask per milestone, withdrawn by a new head;
(playbook 02). How it is checked: whole (gate), one ask per milestone, withdrawn by a new head;
a waiter on one member's pull request wakes only on that member's transition. Whole (issue tracker, knowledge base):
an issue linked to a decision and to a research effort reads both back; a decision linked to its milestone reads it
back. Live: research 085's milestone's record reads back research 085 to its decisions, its milestone, its issues,
its pull requests and their reviews with no text match.
back. Live: research 085's milestone's record reads back research 085 to its decisions, its milestone, its issues and
its pull requests with no text match.
**The status view's tools** read the keys and change nothing: `assigner_wants`, `assigner_want`,
`assigner_differences` and `node_haves`, beside the indexer's and the builder's. Its events are `delivered`,
@@ -1926,9 +2039,10 @@ list:
edges with no code change elsewhere, and a search of the delivery parts' code finds no other module's name.
28. **Waits stay within their step.** A cycle among waits within one step is refused; an acting part acting on
itself, and two acting parts acting on each other in different steps, pass.
29. **A failed acting part leaves the mesh on what runs.** A build module whose new build fails, and one that does not
run within its bound, each leave the modules it builds on their running builds and raise the condition naming
it, which clears when the wait ends.
29. **A failed acting part leaves the mesh on what runs.** A build module whose new build fails, and one whose wait
passes its bound, each leave the modules it builds on their running builds, say `wait-held`, and raise
`acting-part-late` naming it; the holder's release tool moves an entry on, says `wait-ended` and writes the
hand-act log, and the condition clears.
30. **A change is delivered as its difference.** A group that changes the indexer, the checker and a module that
needs both is delivered with no delivery ahead of it, each acting part's new version first in its own step, a
node asleep throughout catching up when it wakes; a fixture change with a new fingerprint and an empty difference
@@ -1936,42 +2050,62 @@ list:
31. **The registry group checks itself.** The registry group passes its one check with its own checker, validator and
composer.
32. **One view.** A pull request adding a seat and a module using it is validated by the indexer's candidate pass and
checked by the checker with the same definitions, and a search of the readers' code finds no compiled seat list.
checked by the checker with the same definitions, and a search of the readers' code finds none of the reads the
revision view replaces, each by name (*Their tools*).
33. **Two graphs, one source.** A revision of the assignment graph computed again from its named inputs equals the one
kept.
34. **One record traces a delivery.** Stopping and restarting the gate in the middle of a delivery leaves the record
complete, with no outcome counted twice, and one call traces an issue of the registry milestone to each node's
running build, also after six later merges; a group approved before the switch to the records is not asked again.
35. **The trace is links.** Research 085's milestone reads back from research 085 to its decisions, its milestone,
its issues, its pull requests and their reviews with no text match.
36. **The milestone can be undone.** A revert of research 085's milestone passes its check under the milestone's own
checker (research 085, Direction, One milestone).
its issues and its pull requests with no text match.
36. **The milestone can be undone, whole.** In a whole-module fixture of the indexer and the assigner, milestone G's
versions take in revisions and write wants, then the versions of before it are started in their place: they read
the per-module keys the new ones kept writing, compute the same wants, and write no want naming a fingerprint
older than the one each node runs; the gate rolled back the same way asks no approved group again.
37. **Nothing is delivered ahead.** Research 085's milestone is delivered with no part delivered ahead of it, read
from its delivery record (ADR 0656, How it is checked).
38. **A newer indexer is reached.** A merged revision naming a newer indexer gives, from the running indexer, a catalog
graph revision holding that indexer's entry; the new indexer is wanted, granted, started, and takes the revision in.
39. **Revisions are kept, and stores say their fill.** A revision pinned by two records stays until both free it, one
neither pinned nor current goes, a `freed` said while its holder was stopped is applied after its restart, a record
past its 90 days is removed and frees its revisions, a store past 80 % raises `store-filling`, and a full one
refuses loudly, naming the part.
40. **Nobody says what is not theirs.** A `running` event on another node's subject, a `freed` from an account other
than the gate's, and a `run` ask from an account other than the checker's are each refused by the bus.
41. **A checking program is built once and judged from outside.** Two ordinary checks with the same checking program
ask one build of it; a change whose checking program drops a rule and hides it is still named in its verdict and
Approve ask.
Each change ADR 0656 makes in an earlier record, and its test:
38. **The want comes from the assignment graph** (ADR 0571 §8). A node-engine's want is written only from a trunk
42. **The want comes from the assignment graph** (ADR 0571 §8). A node-engine's want is written only from a trunk
revision's assignment graph: a pull request's graph leaves every node's want unchanged, and a placement-only
revision moves the want of the module it places (whole, assigner).
39. **A module changed with its build module is built once, by the new one** (ADR 0603 §1, §3, ADR 0614 §1, §6). A
group changing the shared contracts and the build module that builds them is delivered, the contracts built by
the running build module first and the rest by the new one, each module built once, with no build by the running
recipe for a module the new build module builds (whole, assigner and builder).
40. **The graph is kept per revision** (ADR 0603 §2). A source revision taken in gives one catalog graph revision and
its assignment graph revision, kept as objects in the old `objects` sections, and a revision a delivery record
pins is not removed; no key per module with five past values is written (whole, indexer and assigner).
41. **The tiers are a view of the difference** (ADR 0603 §6). The view's tiers of a group equal those read off its
43. **A module changed with its build module is built once, by the new one** (ADR 0603 §1, §3, ADR 0614 §1, §6). With
three build nodes and the new build module updated on the first node first, a group changing the shared contracts
and the build module that builds them builds nothing with the new build module while one node of three runs it,
builds the contracts with the running one, and once the agreed toolchain names the new build module (ADR 0584
§1) builds each other module once, by it, with no build by the running recipe of a module the new build module
builds; the third node builds nothing of the language until it reports the agreed version (whole, assigner and
builder).
44. **The graph is kept per revision** (ADR 0603 §2). A source revision taken in gives one catalog graph revision and
its assignment graph revision, kept as objects in an `objects` section in the old form, and a revision a delivery
record pins is not removed; every part reads the revisions, and the per-module keys are written only for a
rollback, read by no part of milestone G (whole, indexer and assigner).
45. **The tiers are a view of the difference** (ADR 0603 §6). The view's tiers of a group equal those read off its
difference by `difference`, with no other source read (whole, view and assigner).
42. **A check fails by name** (ADR 0603 §10, ADR 0614 §7). A fixture change whose acting part does not build, one
46. **A check fails by name** (ADR 0603 §10, ADR 0614 §7). A fixture change whose acting part does not build, one
whose checking program does not run, and one whose build module invokes a verb no running holder serves, each fail
their check naming the part, and no module of them is composed from its manifest (whole, checker).
43. **A part waits for the revision view** (ADR 0636 §1, §4). With the indexer stopped, the checker, the builder and
the grants part each keep running what they run, say they wait, naming the revision, and act once it is back; a
candidate's heads are read by the indexer's candidate pass and by no running part (whole).
44. **A process reports its handed build** (ADR 0639 §7). A part started while its node's want differs from what runs
47. **A part waits for the revision view** (ADR 0636 §1, §4). With the indexer stopped, then with the assigner stopped,
and on a cold control-node whose parts all start with no view, the checker, the builder and the grants part each
keep running what they run, answer `waits` with the revision and since when, write `view-waits` after five minutes,
and act once the view is back, clearing it; the node-engine waits only for the catalog half; a candidate's heads
are read by the indexer's candidate pass and by no running part (whole).
48. **A process reports its handed build** (ADR 0639 §7). A part started while its node's want differs from what runs
reports the build it was started with, and `node_process_builds` reads it (whole, node-engine).
45. **The forge bridge reads a group from the gate's verb** (ADR 0633 §4, §5). The forge bridge sets the group on each
49. **The forge bridge reads a group from the gate's verb** (ADR 0633 §4, §5). The forge bridge sets the group on each
merged message and publishes a group's end again from the seat `mesh-delivery`'s verb `groups`, and its account
holds no read of the gate's `merges` state, so a read of it is refused (whole, forge bridge).
@@ -2022,12 +2156,12 @@ Each change ADR 0656 makes in an earlier record, and its test:
module names and its cases for a change to the manifest validator and to the Go build module (ADR 0656); a build
module's edge as a trigger, with the extra build per recipe change (ADR 0603 §3, as ADR 0656 changes it); composing
from manifests a module whose changed build module the check cannot run (ADR 0603 §10 and ADR 0614 §7, as ADR 0656
changes them); the module graph's one key per module with five past values (ADR 0603 §2, as ADR 0656 changes it);
the tiers worked out from five live sources (ADR 0603 §6, as ADR 0656 changes it); a seat list compiled into a
part, and every read of another module's bus state the revision view replaces (ADR 0656); the gate's `merges` and
`turns` as its own state, carried into the delivery records and kept current beside them until a later change
removes them; and the review line in a pull request's description, once the verb `review` runs
(ADR 0496 §3).
changes them); the module graph's one key per module with five past values as what parts read (ADR 0603 §2, as
ADR 0656 changes it); the tiers worked out from five live sources (ADR 0603 §6, as ADR 0656 changes it); a seat
list compiled into a part, and every read of another module's bus state the revision view replaces (ADR 0656); and
the gate's `merges` and `turns` as its own state, carried into the delivery records.
- At milestone H's delivery: the writing of ADR 0603 §2's per-module keys beside the revisions, and of the gate's
`merges` and `turns` beside the records, kept until then only so that a rollback of milestone G reads current state.
## Milestones
@@ -2078,7 +2212,8 @@ release reports the controller's code lines against research 063's baseline (acc
| L6 | Provisioning leaves the controller (ADR 0570): every provision is one key of the seat `mesh-provisioner`, held by mesh-provisioner, holding a reference and never a value, and each placed module's provisioning plan a key of its state `plans`, both composed from placement, the wants and the manifests on change events; each provider acts once all a provision depends on is provided; the assigner moves a want only once its build's provisions are provided; a provision no want needs is retiring, and its account or credential is removed only on the operator's Approve, its data never; the vault mints each provision's secret sealed to the consumer's and the provider's node, and the consumer's node-engine writes it; a wait for a person is a provision's state, which the watchman reads; bus permissions stay with the grants part (ADR 0349, ADR 0230, ADR 0547 §11) | — | mesh-sdk, the node-engine (`mesh-host`), mesh-tools (the tool runner's provisioner client), mesh-catalog (mesh-provisioner, the vault, the provisioner client, postgres, keycloak, gitea, route-adapter), mesh-delivery (assigner, watchman) | 1954 |
| L7 | History and records leave the controller (ADR 0571): the builder keeps its build log, one entry at a build's start and one at its end, from which build history and durations are read and the module mesh-catalog asks the builds it missed; the artifact sweep is the seat `mesh-artifact-sweep`, held by artifact-store-tools beside the artifact store, its keep set from the store's own listing, the wants and the build log, sweeping at each build's end from the lease write and recording each deletion in the hand-act log; disk readings are disk-load's own state on every node; who holds which seat is the state `seat-holders` of the seat `mesh-placement` (now `mesh-module-assigner`), and tools are found by the tool runners' announcements; what a node runs is its want and its have; the grants part writes `grants.<module>.<fingerprint>` and keeps memberships in its own state; root-free is a probe of the self-check; no manifest but the controller's reads, invokes or consumes `mesh-controller`, but a named list kept to the lease write, so the lease write's check passes (ADR 0189, ADR 0547 §11, ADR 0549 §6) | — | mesh-sdk, mesh-controller (its pin only), mesh-delivery (builder, indexer, assigner, grants part, view), mesh-catalog (artifact-store-tools, disk-load, the backup seat's holder, mounts, nfs-server, minio, mesh-watcher, the messenger, the self-check, the module mesh-catalog, docker), mesh-tools (the tool runner) | 1955 |
| B0 | The bootstrap delivery: a delivery group's check runs a checking program built from the group's own heads in the build seat's sandbox, and hands the manifest validator and the composer the heads' one seat list; it brings no seat definition, so the running checker checks it (ADR 0644) | 26 needs it | mesh-delivery (checker), mesh-catalog (mesh-build-go), mesh-delivery-contracts (the seat `node-build-go`'s ask `run`) | 1921 |
| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); the registry milestone's reviewed heads are joined to its group as members | 26 to 45 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events), mesh-delivery (indexer, assigner, checker, builder, grants part, gate's view, forge bridge), mesh-catalog (the gate, the build modules, mesh-issues, mesh-knowledge), the node-engine (`mesh-host`), hq | 1966 (with the registry milestone's heads), 1961, 1660, 1962, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 1782 |
| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); the registry milestone's reviewed heads are joined to its group as members | 26 to 49 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events), mesh-delivery (indexer, assigner, checker, builder, grants part, gate's view, forge bridge), mesh-catalog (the gate, the build modules, mesh-issues, mesh-knowledge), the node-engine (`mesh-host`), hq | 1966 (with the registry milestone's heads), 1961, 1660, 1962, 1972, 1721, 1783, 1785, 1794, 1791, 1373 |
| H | The old keys go: once milestone G has run for one record retention (90 days) with no rollback, the indexer stops writing ADR 0603 §2's per-module keys, the assigner reads none, and the gate stops writing `merges` and `turns` (*Two graphs, one source of truth*, *Rolled back*) | — | mesh-delivery (indexer, assigner), mesh-catalog (the gate) | 1921 |
An issue is in one row only (ADR 0493 §2): where a row named an issue another row names too, the last row keeps it and the
earlier row says so in words (the operator, 2026-10-26). Milestone M1 makes each row a record of the issue tracker
@@ -2155,11 +2290,12 @@ order inside one milestone, for this one case (ADR 0644).
## What is not decided here
- Whether mesh-sdk's runtime decoder refuses an unread field in a seat definition today was not read; it decides
whether `acts` alone would rebuild every module.
- Who may review a change, and the review verb's grant: issue 1782, outside milestone G; until it is built the review
stays the line in a pull request's description (ADR 0496 §3).
- What a process started by a node-engine that hands no fingerprint does beyond saying `own build unknown`, for a part
other than the indexer.
- Whether a catalog graph revision has a size bound of its own, as the assignment graph revision's is tested.
- The caps' and the growth's numbers are estimates (*Two graphs, one source of truth*); the fill tools read the real
ones once milestone G runs, and a cap is changed in its manifest.
- How long the checker's stream `checks` keeps its events (*A try*, above).
- Who is granted `checker_try` and the take-in hand tools: the operator's grants, outside this design.