diff --git a/00-META/glossary.md b/00-META/glossary.md index 31c8ffc5..7f676c51 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -291,11 +291,17 @@ every other domain has something to run on. **Recorded by** the controller and t **Purpose.** To decide, send and apply what each node runs, and to say why. **Recorded by** the controller's `inventory` database; from ADR 0567, by the seat `mesh-module-assigner`, in its states on the bus. **Upstream of** Provisioning, Connectivity, Data -and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261, 0411, 0539, 0549, 0559, 0565, 0567, 0571, 0634. +and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261, 0411, 0539, 0549, 0559, 0565, 0567, 0571, 0634, 0656. **Uses:** node, machine, module, seat, manifest (Module), config value (Config), proposal (Operator and conversation). - **assignment** — a module put on a node. Its config values on that node (module-assignment config, Config) are `mesh-config`'s, never part of what the controller records (ADR 0348 §9). +- **assignment graph** — the module versions placed on nodes at one revision, with the edges between versions that + its inputs give: provisioning, contribution, and the acting parts' versions that are to act on each entry. The + assigner computes it from the catalog graph, the placements and the nodes' capabilities, and nothing else writes it; + it keeps each revision naming the revisions of its inputs, served by the seat `mesh-wants`, and writes the wants + from a trunk revision's alone (ADR 0656). Not the **catalog graph** (Delivery), whose entries are modules, not + versions on nodes; not an **assignment**, one module put on one node. - **placement** — which module runs on which node, with its pins, host ports and bindings, and what a person decided of the node, as one key per node in the state `placement` of the seat `mesh-module-assigner`. It changes only by a placement proposal the operator approves, and no repository holds it or names a node (ADR 0567). Until ADR @@ -515,7 +521,7 @@ and the operator act. **Recorded by** the controller's `identity` and `licences` back when it fails. The domain is continuous integration and delivery, named **delivery**, and its parts live in the repository `mesh-delivery` (ADR 0429 §2). **Recorded by** the `mesh-delivery` module (deliveries) and the controller (the planner, the rollout) until release 6 of ADR 0429; from each release on, by the parts it builds. **Downstream of** Module, Core, Health and repair and Data. -**Decided by** ADR 0162, 0218, 0236, 0237, 0238, 0239, 0250, 0267, 0270, 0276, 0323, 0401–0408, 0428, 0429, 0444–0447, 0524–0530, 0539, 0548, 0551, 0571, 0578, 0579, 0593, 0601. +**Decided by** ADR 0162, 0218, 0236, 0237, 0238, 0239, 0250, 0267, 0270, 0276, 0323, 0401–0408, 0428, 0429, 0444–0447, 0524–0530, 0539, 0548, 0551, 0571, 0578, 0579, 0593, 0601, 0644, 0656. **Uses:** module, node, declaration and send (Placement), health (Health and repair), person. - **merge check** — what a pull request carries before it may merge, judging the merge of its head into a @@ -524,11 +530,36 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel **composition check**; for a delivery group, one verdict on every member's head (ADR 0445, ADR 0447). Before release 4 of ADR 0429 a pull request carried two statuses, one for the modules the change touches and one for the repository's own script (ADR 0238). -- **module graph** — the edges the indexer keeps between modules, from which each delivery part orders a source - revision by its own rule: provisions, bus state, a served verb and a reader's capability are waited on; a package - and a build module's change are triggers that rebuild, never waits; a verb edge on a cycle is left out (ADR 0603 - §1, §2, ADR 0614). Nothing stores the order. +- **catalog graph** — the modules as their manifests declare them at one revision (bases, packages, provides and + requires, invokes and serves), with the acting rules the seats declare; the indexer keeps it, one + revision per source revision it takes in (ADR 0656). Its other name is **module graph**, the word for it before + ADR 0656, from which each delivery part orders a source revision by its own rule: provisions, bus state, a served + verb and a reader's capability are waited on; a package, and until research 085's milestone a build module's + change, are triggers that rebuild, never waits; a verb edge on a cycle is left out (ADR 0603 §1, §2, ADR 0614); from research 085's milestone an acting part's edge is a wait within + its step (ADR 0656). Nothing stores the order. Not the **assignment graph** (Placement), computed from it. *Not:* ~~build graph~~ +- **acting part** — the holder of a seat whose definition carries a rule: the step its holder acts in and the modules + it acts on (ADR 0656), in the field `acts` (design 47): the indexer, the checker, the build modules, the + node-engine, the grants part, the assigner. Within a revision its new version acts first in its step, a wait with a + bound (ADR 0656); when it fails or is late, the modules it was to act on stay on their running builds and a + condition names it (design 47). Not every + **part**: an acting part may be any module that holds such a seat, a build module among them. +- **graph difference** — a change's assignment graph against the current one: the new versions, the moved edges, + the touched nodes and the order read off the edges. It is the change's delivery, and nothing stores a plan beside + it (ADR 0656); the assigner serves it by the seat `mesh-wants`'s verb `difference` (design 47). Not a **delivery plan**, + which was stored and handed out. +- **revision view** — what every part acts on: the catalog graph with its heads, seat definitions, verdicts and + 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). 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 + else orders by it. Not a **delivery**, one commit on its way. +- **checking program** — the rules every check applies, built from the change's heads (ADR 0656, ADR 0644) and run in + a build seat's sandbox (ADR 0448 §2) with the revision view as its input and no bus account; the running checker + only asks for it, reads its verdict, records and publishes it (design 47). Not the **checker**, the part that asks for it. - **composition check** — whether the machines a change touches still compose with it, run by the assigner from the modules the indexer and the builder hold, with the composition of `mesh-delivery-sdk`, the one the controller composes with diff --git a/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md b/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md index afe743bd..e3cee7bb 100644 --- a/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md +++ b/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md @@ -1,9 +1,11 @@ --- layer: to-be status: in-progress -code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, hq 00-META/checks] -updated: 2026-11-05 +code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, mesh-contracts, hq 00-META/checks] +updated: 2026-11-06 decisions: + - "ADR 0656" + - "ADR 0644" - "ADR 0639" - "ADR 0638" - "ADR 0637" @@ -12,15 +14,20 @@ decisions: - "ADR 0634" - "ADR 0633" - "ADR 0632" + - "ADR 0619" + - "ADR 0618" - "ADR 0615" - "ADR 0614" + - "ADR 0612" - "ADR 0603" - "ADR 0601" + - "ADR 0597" - "ADR 0596" - "ADR 0595" - "ADR 0594" - "ADR 0593" - "ADR 0590" + - "ADR 0584" - "ADR 0572" - "ADR 0571" - "ADR 0570" @@ -45,6 +52,7 @@ decisions: - "ADR 0526" - "ADR 0525" - "ADR 0524" + - "ADR 0496" - "ADR 0494" - "ADR 0493" - "ADR 0491" @@ -69,6 +77,7 @@ decisions: - "ADR 0452" - "ADR 0450" - "ADR 0449" + - "ADR 0448" - "ADR 0447" - "ADR 0446" - "ADR 0445" @@ -96,9 +105,11 @@ decisions: - "ADR 0401" - "ADR 0398" - "ADR 0395" + - "ADR 0391" - "ADR 0382" - "ADR 0377" - "ADR 0364" + - "ADR 0363" - "ADR 0362" - "ADR 0360" - "ADR 0350" @@ -114,6 +125,7 @@ decisions: - "ADR 0250" - "ADR 0239" - "ADR 0238" + - "ADR 0235" - "ADR 0233" - "ADR 0214" - "ADR 0201" @@ -137,6 +149,21 @@ nodes once it runs healthy there (ADR 0429 §6, ADR 0539); and one chain, from m to want to have, is kept in the bus's key-value store as its only source (ADR 0406, ADR 0408, ADR 0429 §17). A running delivery never touches merging: a merge moves the source revision, and the wants follow it. +**From research 085's milestone, every change is checked and delivered by the acting parts built from its own +heads** (ADR 0656, which supersedes ADR 0645 to ADR 0648). The acting parts, the parts that act on other modules' +changes, declare in their seats' definitions what they act on and are versions in the module graph like any module. +The indexer keeps the **catalog graph** (the modules as their manifests declare them) and the assigner computes the +**assignment graph** (module versions placed on nodes, with the edges between versions), each per revision; a +change's **graph difference** against the current assignment graph is its delivery, its acting parts' new versions +acting first within a bound; its check runs on the acting parts built from its heads and fails, naming one that +cannot be built or run; every part reads one **revision view**, served by the indexer and the assigner; and the gate +keeps one **delivery record** per milestone's delivery (ADR 0656). One delivery goes before that milestone, the only +delivery ever made ahead of a change (ADR 0644). ADR 0656 answers one question and leaves how each part is built to +this design (the operator, 2026-11-06: one record per question, the choices within it the design's own, written with +their reasons). A statement in the sections that follow with no record beside it is such a choice; *Choices this +design makes under ADR 0656*, under *Two graphs, one source of truth*, gives each one's reason. *Two graphs, one +source of truth* and *The check on the change's own acting parts* hold the whole of it. + This is the delivery of [to-be 10](10-delivery.md) carried out. To-be 10 says what is built: the comparison of source and artifacts decides, and nothing here changes that. This design says who answers for one commit's journey, in what order the steps are allowed, and where each fact of it is kept. It was rewritten on @@ -155,7 +182,9 @@ source revision is derived from the keys: every **want** equals its module's **f forge. - **The indexer** computes each module's fingerprint at the merge preview and writes the candidates of each group and the modules of the trunk. It reads no placement: placement is not in any repository, and the forge - bridge carries nothing of placement (ADR 0567). + bridge carries nothing of placement (ADR 0567). From research 085's milestone it keeps the catalog graph per + revision and serves its half of the revision view by a verb of its seat `mesh-catalog` (ADR 0656, + ADR 0656). - **The builder** claims a build by creating its key, once per fingerprint, and orders it as an ask on the stream of the artifact's language; it makes no artifact's bytes since R4e and chooses no node, because the build modules pull the asks (ADR 0498 §1, ADR 0525 §1, §4). From R4g it delivers its own module, the build modules and the controller from its keys, and nothing falls back to build-agent, which is removed @@ -170,11 +199,15 @@ source revision is derived from the keys: every **want** equals its module's **f taking the kept verdict of the rest; it writes the change's one status (ADR 0444, ADR 0445, ADR 0447); it runs dark in R4c-5 (ADR 0484). It judges a delivery once per set of heads, and carries a verdict to a newer trunk commit by its package digests instead of judging it again (ADR 0548 §1, §5; *A move of the trunk carries every verdict*, below). + From research 085's milestone its judging rules are a **checking program** built from the change's heads (ADR 0656) + and run in a build seat's sandbox (ADR 0448 §2); the running checker only asks, records and publishes (*The check + on the change's own acting parts*, below). - **The gate** merges a group's pull requests once check, review and the operator's Approve agree on the same heads, and then moves the mesh's source revision in one write. While the check gates merges, it holds an approved merge for another delivery's running check of the repositories the merge goes into, and keeps the merge's **merge turn** there (ADR 0548 §2, §3, as ADR 0551 §1 bounds them; *Nothing merges into a repository - under check*, below). + under check*, below). From research 085's milestone it keeps one delivery record per milestone's delivery as a + ledger instance (ADR 0656), its `merges` and `turns` carried into the records (*The delivery record*, below). - **The teller** carries conditions and asks to the operator's phone and is the only writer of an ask's answer. - **The assigner** writes which fingerprint each node should run, by six rules: the build is done, the fingerprint is not held, the node is the module's **canary** or the canary runs it healthy, at most `step` @@ -193,7 +226,10 @@ source revision is derived from the keys: every **want** equals its module's **f R4g with release 5; from release 6 it writes no difference (ADR 0524 §4, ADR 0528, ADR 0539 §8). The check composes no placement and waits for no node's answer: a pull request changes no placement, since placement is bus state 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. + 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 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 mesh-config, its secrets and bus credential asked of the vault sealed to the node's key, its licences and what @@ -370,6 +406,99 @@ compare-and-set (ADR 0406 §4). It writes a new head when the forge says one, an pull request's verdict is carried to it or a whole check is asked against it (ADR 0548 §5, §7). A running check watches that key and stops only when the head in it moves; a trunk move never ends it (ADR 0548 §1). +### 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 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 +holds: + +- **the group's state**: the heads, the verdicts, the checks and the asks, which `merges` and `turns` hold today; +- **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, 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), 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. It is written only while no other condition covers the record: while + an entry of it is held by a wait that raised `acting-part-late`, or a store that holds it raised `full`, that + condition already tells the operator why the record is open, and a second one for the same cause would only be noise. +- **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. +- **An Approve given holds at its heads.** An Approve the operator gave for a group's heads holds while those heads + stand: a check of the same heads asked again, after a main move or for any other reason, records its verdict in + the record and asks the operator nothing again (issue 1972). The Approve is withdrawn only as ADR 0441 §1 has it, + when a member stops being mergeable, and a carried verdict is read as current (ADR 0548 §8). +- **A small fix has a record of its own.** A small fix is a milestone of one issue (ADR 0493 §3), so it has a + 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. 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.** A record's state of 30 modules on four nodes is about 30 KiB, against the + bus's 256 KiB value limit (research 085, Findings 8). The ledger keeps every operation beside that state: 30 modules + on four nodes with seven outcomes each is about 840 outcome operations a record, at about 0.2 KiB each about 170 KiB, + so a record costs about 200 KiB with its snapshot. At an estimated 20 deliveries a day (a guess from the days of + research 085, not a measurement) the ledger grows about 4 MiB a day, about 360 MiB at the default retention; its cap + is 768 MiB, twice that, so a busier month than estimated still fits. Removing a record at its retention removes its + operations and its snapshot with it, so the ledger's size follows the retention, not the mesh's age. +- **The switch carries today's state in, once.** At the new gate's start, `merges`, `turns` and every group in + flight become records, and from then on the gate writes `merges` and `turns` no more. The switch is safe to run + twice: a group in flight becomes a record keyed by its milestone and heads, and a switch that finds that record + already there leaves it as it is, so delivering milestone G again after a revert makes no second record and asks + nothing again. A revert of the gate is the delivery system's restore of its kept data (*A module's kept data on the + bus, backed up and restored with its delivery*). +- **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 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, 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.** 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 milestone G is delivered again following a revert; 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 *ADR 0539 §17.* @@ -403,9 +532,353 @@ verdict that names the trunk's head (*A move of the trunk carries every verdict* backup hold, superseded, covered, stopped, retired and failed) and the machine steps went at release 6: a module's progress on each node is its want against its have, shown by the view (ADR 0539 §17). +## Two graphs, one source of truth + +*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, +§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 +graphs answer them, the second computed from the first, so there is one source of truth (ADR 0656; research 085, +Findings 11). How each is kept, read and retained is this design's choice under ADR 0656. + +``` + manifests ──► indexer (mesh-catalog) ──► catalog graph revision ──┐ + ├──► assigner (mesh-wants) ──► assignment graph revision + placements (mesh-module-assigner) ───────────────────────────────┤ │ │ + node capabilities (node-engine seat) ────────────────────────────┘ │ └─► wants, from a trunk revision only + ▼ + a change's assignment graph − the current one = its graph difference = its delivery +``` + +- **The catalog graph is the indexer's.** The indexer, holder of the seat `mesh-catalog`, keeps the modules as their + manifests declare them; it is today's module graph, and *module graph* stays its other name (ADR 0656). At each + revision it holds one entry per module: its bases, its packages, the toolchain version and recipe version of the + build module that builds it as ADR 0603 §2 has them, its provides and requires, its invokes and serves, and its + 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). 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 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 as the graph's form (ADR 0656, which + changes ADR 0603 §2); the keys are written no more, and a revert of milestone G restores them from their backup + (*Reverted*, 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 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. +- **An unchanged entry is stored once.** A revision is kept as its list of entries' digests, and each entry as an + object of its own named by its digest, so an entry that two revisions share is stored once; an entry object is + removed when no kept revision lists it. Consecutive revisions differ by the few entries a delivery moves, so this + keeps the growth to what changes. It is chosen over a larger cap because whole revisions would grow with every + revision whatever changed, and a cap would only delay the same fill. +- **The caps and the growth** (estimates, to be read from the fill tools once the milestone runs). A whole 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. A delivery moving 5 modules on four nodes adds about 20 assignment entries of about 1 KiB and a + list of about 15 KiB, and 5 catalog entries and a list of about 4 KiB: about 0.7 MiB a day of assignment graph and + 0.2 MiB of catalog graph, about 63 MiB and 18 MiB at the records' default retention of 90 days. The caps are 512 MiB + for each, many times the steady state. 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. When a removal brings the store back below its cap, + the part says `room` of its seat, takes the refused work up again (the pending merge, the next revision, the held + record) and the condition clears; a condition that never clears would hide the next fill. 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..` from a trunk + 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). +- **Reverted.** Only the new form is written: the indexer, the assigner and the gate keep nothing of ADR 0603 §2's + keys or of `merges` and `turns` beside the new forms, since nothing reads them after milestone G. A revert of + milestone G is a change like any other, checked and delivered, and the delivery system restores what it must + (*A module's kept data on the bus, backed up and restored with its delivery*, under *A backup before the apply*). + Milestone G changes the form of these kept data, and raises their `form` (a kept data declaring none reads as form + 0, so G's own move takes their restore points): + + | Part | Kept data before G (form 0) | After G | Restored at a revert | + |---|---|---|---| + | the indexer | the state `modules` (ADR 0603 §2's one key per module) and its durable consumer's position on the forge bridge's stream `forge` | catalog graph revisions as objects, form 1; `modules` no longer declared | yes: `modules` as it stood at G's delivery, and the consumer's position | + | the gate | the states `merges` and `turns` | the delivery record's ledger, form 1; `merges` and `turns` no longer declared | yes: `merges` and `turns` as they stood at G's delivery | + | the assigner | the wants, written from the indexer's `modules` | the same wants, form 0, written from the assignment graph; the assignment graph revisions as objects, form 1 | no | + + The wants keep their form: their keys and values are unchanged, only what the assigner computes them from changes, + so G raises no `form` on them and a revert restores none. Restoring the wants would send nodes back to older + fingerprints until the reverted assigner computed them again; leaving them, the nodes keep what runs and the reverted + assigner writes them again from the caught-up indexer. The assigner's placement and every other kept data of the + three keep form 0 and are never restored, so nothing decided since (a placement, a verdict, the source revision) is + set back. + +### A change's graph difference is its delivery + +- **The difference names everything a plan named.** For a change (a pull request's heads, a delivery group's + heads, or a merge), the indexer gives its catalog graph and the assigner its assignment graph. Compared with the + current revision, the difference names the new versions, the moved edges, the touched nodes and the order, read off + the edges, and no plan is stored (ADR 0656). The assigner serves it by the seat `mesh-wants`'s verb `difference`, + for two revisions of the assignment graph or for a candidate against trunk. ADR 0603 §6's tiers, as ADR 0633 §5 + changes it, are a view worked out from that difference (ADR 0656, which changes ADR 0603 §6), read through + `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. +- **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. +- **A candidate's graphs are kept apart, and dropped with its heads.** A candidate's two graphs are computed per + check, kept apart from trunk's, and dropped on the gate's event `heads-replaced`, said when the candidate's heads + 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 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 + first in their own steps (ADR 0656); the one exception is the delivery before research 085's milestone (*The + bootstrap delivery, and the one milestone after it*, under *Milestones*; ADR 0644). + +### The revision view + +- **A pair, each half served by its one owner** (ADR 0656). For a trunk revision, or for a candidate's heads before + any merge, the indexer serves the catalog half by the seat `mesh-catalog`'s verb `revision`: the catalog graph, the + heads, the seat definitions, the manifests' verdicts and the fingerprints. The assigner serves the assignment half by + the seat `mesh-wants`'s verb `revision`: an assignment graph revision, named by its own revision and naming its + 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 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); 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 + form. + +### What ADR 0656 leaves to this design + +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 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 + unknown` in its runtime tool and its journal, and the indexer acts on it as ADR 0639 §8 has it. **Its cost:** one + change of mesh-sdk's runtime, which rebuilds every module once (ADR 0656, Consequences), and one of the + node-engine; the order between them is the composing step's wait of the node-engine (*Order without a person*, + below), so a module whose runtime reads the value is composed only by a node-engine that hands it. +- **The build seat's ask that runs a program in the sandbox.** The checker asks the seat `node-build-go` for an ask + `run` on the build seats' `check` stream: one whole job on one node (ADR 0525 §3), which fetches the named heads, + 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. Only the checker's account may publish `run`. Every step key of a `run` ask starts with the + asker's name (`checker.`), so the checker's read grant is fixed in advance to its own prefix and never + follows ask ids one by one (ADR 0363 §2); 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. Only a + build made in a run step that runs nothing but the build is kept and reused: a program's own run happens in a + separate run step over a copy, so a program that rewrites its own binary while it runs changes no kept build. A + candidate's build is never delivered to a node: a want names only a trunk revision's fingerprint (*Only a trunk + revision writes wants*), and a candidate's build is kept only for checks. +- **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). 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 + +ADR 0656 decides the direction and leaves every choice made in working it out to this design, each with its reason +(ADR 0656, Consequences; the operator, 2026-11-06 20:3x). The choices, and why: + +| Choice | Why | +|---|---| +| 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 are many times the estimated 90 days | +| An unchanged entry stored once, a revision being a list of its entries' digests | consecutive revisions differ by a few entries, so the stores grow by what changes; a larger cap over whole revisions would only delay the same fill | +| No old form written beside the new; a revert restores the parts' kept data on the bus from a restore point taken before they moved, then they catch up forward | only the new form is looked after (the operator, 2026-11-06 21:3x), and a revert still works, as the delivery system's capability for every change of a kept data's form | +| A module's kept data on the bus backed up before a `form` change and restored beside the live data at a revert, a missing restore point holding the revert | ADR 0404 covers only the items on a module's node, so the bus's kept data had no restore point; a restore never goes over live data, and a revert with nothing to restore must not start on the newer form unasked | +| Only kept data whose `form` changes restored, the wants never; at a revert the node-engine last on its node, and the reverted assigner and gate only after the reverted indexer took in the trunk's head | restoring anything else would set back what was decided since; restored wants, or an assigner reading an indexer not yet caught up, would send nodes to older fingerprints; the node-engine of before may not know how to restore | +| A restore point holding the consumers' positions and the catch-up tool the newer manifest declares, called after a restore | the reverted build then hears everything since as after any restart; the stream `forge` keeps 7 days, so what fell out of it is taken in from the trunk by the part's own tool | +| 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 768 MiB, a record's operations removed with it | the traces asked so far went back weeks; about 840 outcome operations make a record about 200 KiB, so 90 days at an estimated 20 a day is about 360 MiB, and the cap is twice that | +| `record-open` only when no other condition covers the record | the cause is already told once; a second condition for it is noise | +| `full` cleared by `room` when the store falls back below its cap | the refused work is taken up again, and a condition that never clears would hide the next fill | +| The switch into records keyed by milestone and heads, safe to run twice | delivering milestone G again after a revert makes no second record and asks no group again | +| 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 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 | +| Composing waits per node, held by the assigner, with no new field in the want: a node's node-engine want first, its other wants once its new node-engine runs | a node away holds only itself (ADR 0603 §7); an old node-engine reads its want as it is, so it is reached with no form it cannot read | +| 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 starting with the asker's name | the program is a long job pulled where capacity is, run in the sandbox every check already uses (ADR 0448 §2), and no other part can have a program run or read its verdict | +| A program built once per fingerprint and reused, only from a run step that runs nothing but the build; a candidate's build never delivered | a check that leaves the checking program unchanged costs no build, and a program run from a change can never change a kept build or reach a node | +| 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) | +| Every change of the checking program's fingerprint named in the verdict and the Approve ask, with the rule names of the trunk's and the heads' programs compared by the running checker outside the sandbox as its detail | only the fingerprint covers a helper a rule calls; 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, with its own read and release tools and the events `wait-held` (at the wait's start, with its bound) and `wait-ended`, timed by the watchman, whose clock starts when the new version's build is done | the part that does the step already holds its state; the watchman timing from `wait-held` still raises a wait whose holder stopped, and counting from the build done leaves a slow build to the build's own wait | +| 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, held and failed outcomes, end and `record-ended`, the finding `record-open`, node away and wake, switch done once, 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 switch run twice must 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, 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. + +### What each step says + +Each step says what it did as its own event, the event of the part that did it. A catalog graph revision kept +carries the source revision; an assignment graph revision kept and a difference worked out carry the source revision +and the assignment graph revisions; an entry taken in, built, checked, composed, wanted and running also carries the +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..event.` (ADR 0363 §1, §2). A seat's event is +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 | +|---|---|---|---|---| +| a catalog graph revision kept | `catalog-revision` | `mesh-catalog`, the indexer | `mesh.seat.mesh-catalog.event.catalog-revision` | the seat's definition in `mesh-delivery-contracts` | +| an assignment graph revision kept | `assignment-revision` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.assignment-revision` | the seat's definition in `mesh-placement-contracts` | +| a difference worked out | `difference` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.difference` | the same | +| an entry taken in | `take-in.taken`, which gains the entries | the indexer's own | `mesh.stream.mesh-delivery-indexer.event.take-in.taken` | the indexer's manifest | +| 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 node's capabilities changed | `capabilities-changed` | `node-engine`, the node-engine on that node | `mesh.seat.node-engine.event.capabilities-changed.` | the same | +| a wait held, and ended | `wait-held` (step, acting part, its version, the entry, since when, its bound), `wait-ended` (the same, and how: ran, released) | each holder: `mesh-wants` for the assigner (placing, wanting and composing), `mesh-grants` for the grants part, the builder's own stream | `mesh.seat..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, full, and back below its cap | `filling`, `full`, `room` | `mesh-catalog`, `mesh-wants`, `mesh-delivery` | `mesh.seat..event.filling`, `.full`, `.room` | each seat's definition | +| a record pins or frees a revision; a candidate's heads replaced; a record ended | `pinned`, `freed`, `heads-replaced`, `record-ended` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.` | 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..`, 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). 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_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: `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.** 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.`, 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 -*ADR 0419 §1, §2, ADR 0429 §6, ADR 0539 §6, §7.* +*ADR 0419 §1, §2, ADR 0429 §6, ADR 0539 §6, §7; from research 085's milestone ADR 0656, of +which ADR 0656 changes ADR 0603 §1, §2, §3 and ADR 0614 §1, §6.* A delivery group has no order among its members, and nobody writes one (ADR 0419 §1). From release 6 there are no tiers: no plan orders modules into steps. What reaches a node, and when, is the assigner's six rules, read @@ -425,6 +898,110 @@ per module and node from the keys: - **No `after:` line is read.** The gate reads no `after:` line, no refusal asks for one, and a description that holds one is told once on its pull request that the line is not read (ADR 0419 §2). +### 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, §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 +checker checking, the build modules building, the node-engine composing, the grants part granting, the assigner +placing. *What ADR 0656 leaves to this design*, above, gives the rule's fields. Adding one needs its manifest and +its seat's rule, and no code elsewhere: the indexer's map from language to build module and the checker's own module +names go, since every new acting part needed code changes elsewhere (research 085, Findings 2). + +- **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 the bound its rule's `bound` gives (ADR 0656), the part's own as ADR 0603 §8 + has every wait. A bound's clock starts when the new version's build is done, not when the wait begins, so a slow + build of the acting part is the build's own wait and not counted twice. 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 one node), 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. +- **Composing waits per node, by the assigner.** A node's wants wait only for that node's own node-engine, never for + another node's, so a node away holds only itself (ADR 0603 §7). The want gains no field: the assigner, which writes + the wants, holds a node's other wants until the node-engine seat's `running.` says the new node-engine version + runs on that node, and writes that node-engine's own want first. A node-engine of before milestone G thus gets its + own want first and every other want once it runs the new one, so an old node-engine never composes a module it + cannot read. The clock of that node's bound starts when the node is present and its node-engine's want is written, + so a node asleep raises nothing while away. +- **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 + 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). The holder says `wait-held` at the start of the wait, carrying its bound, and the + watchman times it: a holder stopped in the middle of a wait still has its wait timed, so a stopped holder hides + nothing. At its own start the watchman reads the open waits from each holder's read tool, as it seeds + `take-in-waits` from `indexer_pending`, so a wait said while it was down is timed too. 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 + what its new version is built from or needs (a base, a package, a provider) is acted on by its running version, + never waited for: the running build module builds the shared contracts and the registry holder, then the new build + module builds the rest, so ADR 0614 §6's cycle through them stays allowed, now for that reason. +- **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, 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 that is the previous one with only the indexer's entry replaced by the one its manifest at the merge + gives, and nothing else of the revision, so no other want moves before the named indexer takes the revision in; 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, 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 assigner, holding a node's other wants until its new node-engine runs there (*Composing waits per node*) | `assigner_waits` | `assigner_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, + watchman): 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` at the wait's start with its bound, and raise `acting-part-late` + naming it; a holder stopped in the middle of a wait still raises it, and a watchman restarted mid-wait reads the + wait from the holder's read tool and times it; a bound counts from the new version's build done; 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 (assigner, node-engine): during a node-engine change with + one node away, the present nodes take their new node-engine and then their other wants, nothing waits on the node + away and no condition is raised for it; a node asleep since before milestone G wakes, takes its new node-engine + first, then the rest. Whole (indexer, assigner): a merged revision naming a newer indexer gives a catalog graph + revision that is the previous one with only the indexer's entry replaced, and no other want moves; 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 §5). For release 6's own switch, the last plan of release 5's controller, the version-skew edge and that @@ -610,11 +1187,87 @@ From its delivery, a check is this: providers it alters; for a group, once over every member's change, read from the indexer's candidates of the group, and while the indexer holds none the group's check waits and says why. It composes with the function every node agent composes with, the composition of `mesh-delivery-sdk`, so the two agree for one version of that - package; its verdict names the version it composed with (ADR 0635 §4). No composing program is built from a change: a change to the + package; its verdict names the version it composed with (ADR 0635 §4). Until research 085's milestone no composing program is built from a change but at a node-engine member's head (ADR 0636 §4): a change to the composition is proved by its own tests, a field only a newer composition reads goes out as optional first, and the running composition refuses a manifest that uses a field it does not read, naming the field. What it fails on, and its warning past twelve rebuilt modules, are the composition check's, as they were the controller's composition check's (ADR 0446, ADR 0237 §2). The assigner writes the verdict under the checker's hash of the heads it judged, in its own bucket, and the checker reads it into the - composition of its verdict, which says "not run" until the assigner's module exists (ADR 0528 §5). + composition of its verdict, which says "not run" until the assigner's module exists (ADR 0528 §5). From research + 085's milestone the composer is an acting part of the step `compose`: a change to it is built from the change's + heads in the check's first tier and composes with its own code (ADR 0656; *The check on the change's own acting + parts*, below). + +### 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, 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). + +- **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. +- **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). 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 + the validator, the composer and the Go build module already run; a change not yet merged is not trusted. The running + checker alone asks, reads the program's verdict, records it and publishes it. +- **A first tier that cannot build an acting part fails the check, naming it.** The verdict never passes on the + running version's word: a module whose changed build module the check cannot run is no longer composed from its + 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, and the verdict says `waiting for a build node` or `did not build`. +- **A change to the checking program is named, by a part it did not build.** Every change of the checking program's + fingerprint is named in the verdict and the Approve ask, whatever changed: a helper a rule calls changes what the rule + judges as much as the rule does, and only the fingerprint covers every input. The rule-name comparison is its detail: + 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. +- **The asking and recording are covered by the checker's whole-module test**, which starts the changed checker on a + real bus and asks one check of a fixture group end to end (ADR 0391 §1). +- **One seat list for both readers of a group's heads.** Inside a group's check the checker makes one seat list from + 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, 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 change to a helper a rule calls, with no rule renamed, names the checking + program's new fingerprint in the verdict and the ask; a program that rewrites its own binary while it runs changes + no kept build; 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 @@ -651,8 +1304,9 @@ pull request, and it owes the forge nothing. - **Two reads across the seam stay until issue 1966 moves them.** The indexer reads the checker's previews, and the checker watches the indexer's `candidates.`. Both are readers of another module's bus state that ADR 0596 §3, as ADR 0615 changes it, lets stay until their module moves; issue 1966 tracks the move to the owner's tools - or events. The forge bridge's read of the gate's `merges` state (*Taken in by the indexer it names*, below) is - the same kind. + or events, and from research 085's milestone the revision view replaces both (ADR 0656). The forge bridge's + read of the gate's `merges` state (*Taken in by the indexer it names*, below) was the same kind, and goes to the + seat `mesh-delivery`'s verb `groups` (ADR 0656). - **A try event is never lost.** The checker publishes each try event with retry until the stream `checks` acknowledges it, and `try.ended` carries the state on every end; whether it was sent is kept in the try's own state, so an end after a checker restart still sends it. Only the checker's account may publish `try.*` on its @@ -760,6 +1414,10 @@ pull request, and it owes the forge nothing. assigner writes each want when its six rules hold. A merge is never refused, held or dropped once it is made. What of it is taken in waits only for the indexer its source revision names, or for the rest of its group (*Taken in by the indexer it names*, below; ADR 0633). +- **From research 085's milestone, the wants follow the trunk's assignment graph.** A merge taken in makes a + catalog graph revision and then an assignment graph revision; the assigner writes each want from that revision, + its rules unchanged (ADR 0656), and each acting part's new version acts first in its own step (ADR 0656; *Two + graphs, one source of truth*, above). - **Two merges close together are two revisions.** The second's fingerprints replace the first's in the indexer's modules; a node gets the newer fingerprint's want once its rules hold, and a fingerprint nobody wants any more is never applied. Nothing waits for a window to close. @@ -828,7 +1486,7 @@ timer. A group's merges arrive together. changes ADR 0603 §1 and §9). - **A group's merges are taken in together.** The forge bridge sets the group on each member's merged message, from the gate's `merges` state, a read of another module's bus state that ADR 0596 §3 lets stay until issue 1966 - moves it, and once every approved member is merged it publishes `forge.group.merged` on + moves it (from research 085's milestone, from the seat `mesh-delivery`'s verb `groups`, ADR 0656), and once every approved member is merged it publishes `forge.group.merged` on its own stream `forge`, after the last member's merged message, naming the members and their merge commits. The indexer hears both through its one consumer, keeps each member's merged message in `pending` until the group's end, and then takes the members in as one source revision under the first bullet. A refused member is no @@ -849,7 +1507,8 @@ timer. A group's merges arrive together. (take the pending revisions, or a group gathered so far, in with the running indexer, when the named indexer can never run or the group's end never comes) and `indexer_drop_pending` (drop a pending merge a later one replaced), and the forge bridge's `bridge_groups` (read the groups it holds open) and `bridge_group_end_again` (publish a - group's end again from the gate's `merges` state) (ADR 0633 §5). Both of the indexer's hand acts emit + group's end again from the gate's `merges` state; from research 085's milestone from the verb `groups`, ADR 0656) + (ADR 0633 §5). Both of the indexer's hand acts emit `take-in.taken`: a hand take-in as any take-in does, and a hand drop naming the drop, for a merge whose wait was said pending, so the watchman's `take-in-waits` clears; a drop of a merge never said pending emits none (ADR 0633 §5, ADR 0639 §9). @@ -893,6 +1552,99 @@ backup holder backs up nothing, and the have says so. - **What it costs.** About 85% of data modules back up in under 30 s; the slowest measured, postgres on the home server, about 165 s (research 055, finding 5). A module that keeps no backed-up item costs nothing more. +### A module's kept data on the bus, backed up and restored with its delivery + +*ADR 0404, ADR 0235, ADR 0233, ADR 0539 §16, ADR 0595, ADR 0619 §2, ADR 0633 §1, §5, ADR 0363 §2; from research +085's milestone, this design's choice under ADR 0656.* + +**ADR 0404's backup does not cover a module's kept data on the bus.** It backs up the items a moved module keeps on +its own node (ADR 0404 §2, ADR 0233), and the bus's streams are dumped whole by the bus module on its own rhythm +(ADR 0235), not per module and not before a delivery. A key-value state, a stream or an object store a module keeps on +the bus (ADR 0595, ADR 0619 §2), and its durable consumers' positions on the streams it reads, therefore have no +restore point taken before its module moves. That is a gap of the delivery system, and milestone G builds it, because +a revert of milestone G is the first change that needs it. It is built as the delivery system's capability for every +later change of a kept data's form, not as a case of this milestone. + +- **Its form is declared.** Each kept data a manifest declares names a `form`, a number its holder raises whenever it + changes how it writes that data, or stops declaring it; a declaration naming none reads as form 0. The declaration + alone cannot tell a changed layout of the same bucket, so the holder says it. Only kept data whose `form` changes is + backed up and restored, so a part's other kept data, and every decision it holds, is never set back. +- **It is backed up before the module moves.** When a want moves a module whose new manifest declares a kept data at + another `form` than the running one, or no longer declares it, the node agent applying it first asks the seat + `node-backup` of the node holding `mesh-broker` for a restore point of that kept data, and waits for its event + `ended`, as for any backup before the apply. The bus module's account takes it, as it takes the bus's snapshots + (ADR 0235 §5). A restore point is keyed by the module, the kept data, its form and the fingerprint it was taken + before, and it holds the kept data whole, the position of each durable consumer the module reads with, and the + catch-up tool the newer manifest declares beside that kept data. A failed + backup holds that module as any failed backup does. +- **A revert restores it before the reverted build starts.** When a want takes a module back across a change of a + kept data's `form`, the node agent applying it asks that seat's verb `restore` for the restore point of that module, + kept data and form, waits for the seat's event `restored`, and only then starts the reverted build. The node agent + that restores is the one running when the revert starts. As every restore is, it is brought back beside the live data + and never over it: the live kept data of the newer form is set aside under a name of its own, and only the operator + removes it, so nothing written since is destroyed. The consumer's position is set back with it, so the reverted + build hears everything since as after any restart. +- **The order of a delivery that crosses a form change** is the module graph's (*Order without a person*), with three + edges the assigner, which writes every want, keeps: + - the new versions of the `node-backup` holder and of the bus module run before any module whose kept data's form + changes is moved, since the restore point needs both; + - at a revert, a node's node-engine goes back last on that node, after every module it restores there has said + `restored` and runs, since the node-engine of before may not know how to restore; + - the reverted assigner and gate are wanted only once the reverted indexer has taken in the trunk's head, as its + `take-in.taken` naming the current source revision says (ADR 0633 §1), since the assigner of before computes the + wants from the indexer's `modules` and would otherwise write the fingerprints of the restore point. Until then the + running assigner writes no want, and every module keeps the fingerprint it runs. +- **The restored part catches up forward, with the code it already has.** Read on main, mesh-delivery at 2d201f78 + and mesh-catalog at 1d07778c: + - **The indexer** (mesh-delivery, `modules/mesh-delivery-indexer`): its durable consumer `_` on the stream + `forge` redelivers every message since the restored position, and `handle` takes each + merge through `Indexer.Arrive` into `pending` and then into `modules` (`applyMerged`); `applySource` takes the + gate's `source.main` in, and `ApplySource` leaves a revision older than the latest it has seen. **The stream + `forge` keeps 7 days**: the forge bridge's manifest declares no age, and a stream's default is 7 days, at most 30 + (mesh-sdk `go/catalogue` `StreamDefaultDays`, `StreamMostDays`), so it does not cover the records' 90 days. A + merge older than the stream's first message is never redelivered, and the code then leaves that repository's + modules at the restore point's fingerprints. So the restore runs one forward step: once the reverted indexer + runs, the node agent that restored it calls its tool `indexer_take_in` with each repository the gate's source names + (`NewTakeInRepository`, which takes in the head the source names for a repository). The catch-up tool is declared + by the newer build's manifest, beside the kept data whose form it changes, and recorded in the restore point when + the backup is taken; the node agent takes it from the restore point, never from the manifest being applied, since + at a revert that is the manifest from before the change, which names none. So the delivery system names no + module's tool. + - **The assigner** (mesh-delivery, `modules/mesh-delivery-assigner`): it keeps nothing restored. Its pass + (`passer.wants`) writes, from what the world holds now, each node's want by the six rules from the indexer's + newest fingerprint at the trunk, and only where the value differs; so once the indexer has caught up it writes + the trunk's wants, and nothing older. + - **The gate** (mesh-catalog, `modules/mesh-delivery-gate`): `Holder.Load` reads the restored deliveries and groups + back; `readOnStart` then reads what happened while it was away: `wantForge` reads the forge's open pull requests + at the next look, `hearAsks` reads the teller's answer to every ask it still holds as pending, and + `SourceCatchUp` brings the newest merge of each repository into `source.main`, never moving a repository back. An + ask the gate of milestone G raised after the restore point is not among the pending ones, so its answer is not read + and the operator is asked again. Whether the first look clears a merge kept in the restored `merges` that the forge + merged since was not read in the code; the catch-up test below proves it with the real builds. +- **A missing restore point holds the revert, naming it.** When the restore point a revert needs does not exist, or + `restored` does not come, the node agent applies nothing of that module, keeps what runs, and writes the have phase + `held` with the reason `restore point missing` and the module, kept data and form it names; the assigner asks the + operator (hold, or start the reverted build on the live data as it is), raises the condition naming them, and on + the answer writes the node's want again naming it (ADR 0539 §16). +- **What the operator is told.** A revert across a change of a kept data's form puts that kept data back to the + moment before the change moved it: the deliveries made since stay delivered, but what the part wrote there since + (for milestone G, its delivery records and its graph revisions) is set aside and not read again; an Approve asked + and answered since is asked again, and one the gate asked before still reads its answer. The revert's Approve ask + says this in those words, naming each kept data restored. +- **Its tools.** The seat `node-backup`'s `backed-up` lists each module's restore points of its bus data, with the + kept data, the form and the fingerprint each was taken before, and the data set aside at each restore, with its + size; `restore` restores one; `discard-aside` removes data set aside by a restore, only on the operator's call, and + writes the act, who and why in the hand-act log of the seat `mesh-hand-acts`. `backed-up` and `restore` exist on + the seat today and gain the module's kept data on the bus as an item; `discard-aside` is new. +- **How it is checked.** Whole (node-engine, backup holder, bus module): a want moving a module across a `form` + change, and one moving a module whose kept data declares no `form` to one that does, each take a restore point + first, keyed by module, kept data, form and fingerprint; a want taking it back restores it beside the live data and + the consumer's position before the reverted build starts, and the data set aside is listed with its size and + removed only by `discard-aside`, which writes the hand-act log; a revert of a group holding the node-engine and a + module whose form changes restores the module before the node-engine goes back; a revert whose restore point is + missing is held, naming it, and moves on only on the operator's answer. The catch-up is proved with the real builds + of the indexer, the assigner and the gate from before milestone G, never fixtures (acceptance test 36). + ## Compositions, wants and the apply *ADR 0360 §8, §9, ADR 0377, ADR 0429 §4, §6, ADR 0539 §9, §14 to §16, ADR 0547 §2 to §9, ADR 0549, ADR 0555.* @@ -1166,6 +1918,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 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 @@ -1226,6 +1981,32 @@ forge module's files of what it announced (ADR 0406 §6). `times`, `what-if`, `release`, `stop` and `retire-history` went at release 6; each is answered that it no longer exists. +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) | +| `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..`, 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`, `room` | a delivery record ended; the ledger past 80 % of its cap; the ledger full; the ledger back below its cap (*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.** 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), 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 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`, `moving`, `settled` and `stuck` (*A delivery* above). @@ -1402,6 +2183,96 @@ From ADR 0548 and ADR 0551 (issue 568): and the forge's statuses; issue 568 is resolved only once this has passed on the mesh (ADR 0548 §10, ADR 0551 §2). +From research 085 (ADR 0644 and ADR 0656; each section above names its tests and their level). 26 and 30 are ADR +0656's own checks; 38 to 45 check each change ADR 0656 makes in an earlier record; the rest ADR 0656 leaves to this +list: + +26. **The milestone checks itself.** Research 085's milestone's group check runs on the checking program built from + that group's own heads, read from the check's record (ADR 0644, How it is checked), and no member of it was + delivered ahead of it. +27. **A new acting part needs no code elsewhere.** A fixture seat declaring a rule, with a fixture holder, gets its + 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 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 + is refused, naming its module; a group's preview before its merge is its difference. +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 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 and its pull requests with no text match. +36. **The milestone can be undone.** Milestone G's delivery took the restore points of the indexer's `modules` with its + consumer's position on `forge`, and of the gate's `merges` and `turns`, before it moved them. A revert of milestone + G is checked on the checking program built from the revert's own heads and delivered: the indexer's and the gate's + kept data is restored beside the live data before their reverted builds start, the indexer catches up (its + `forge` consumer, then `indexer_take_in` of every repository the source names, the tool taken from the restore + point while the reverted manifest declares no catch-up tool), and only then are the reverted + assigner and gate wanted; a module delivered during milestone G keeps its fingerprint while they wait; no want goes + to a fingerprint older than the trunk's; the operator is asked again only for an ask raised after the restore + point. A revert whose restore point is missing is held, naming it. Proved with the real builds of the indexer, the + assigner and the gate from before milestone G, not fixtures (whole, checker, gate, assigner, indexer, node-engine, + backup holder, bus module). +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 that is the previous one with only the indexer's entry replaced; no other want moves; 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 with its operations and frees its revisions, an entry two revisions share is stored + once, a store past 80 % raises `store-filling`, a full one refuses loudly, naming the part, and says `room` and + clears once a removal brings it below its cap; after one fixture delivery of 30 modules on four nodes, `gate_records` + reads the ledger's fill, about 200 KiB for the record. +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: + +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). +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; no per-module key with five past values is written (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). +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). +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). +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). + ## What retires - The "release branches" and the controller's check of them (ADR 0358, superseded by ADR 0401). @@ -1445,6 +2316,14 @@ From ADR 0548 and ADR 0551 (issue 568): view's events instead; the assigner's comparison with the send; the node-engine's queue of whole declarations, its orders, put-backs and five-minute reconcile; the buckets of plan state; the stop of a shared plan (ADR 0299, superseded by ADR 0539 §18); and a declaration sent by hand (ADR 0526 §4, changed by ADR 0539 §10). +- At research 085's milestone's delivery: the indexer's map from language to build module, and the checker's own + 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 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`, carried into the delivery records and written no more. ## Milestones @@ -1494,6 +2373,8 @@ release reports the controller's code lines against research 063's baseline (acc | L5 | Control records leave the controller, and the controller is not changed (ADR 0565): mesh-config keeps each module's declared config in its state `declared` and publishes the predefined keys, which the indexer reads from the forge bridge's messages with each open pull request's config candidate; config switches to that source only on the operator's take-over, refused while `compare` shows a difference; each node agent writes its node's declared packages (the node seat `node-engine`, verb `packages`); the assigner holds the new seat `mesh-wants` with the upgrade policy a person chose and the bus step; the hand-act log (`hand-act`, `hand-acts`, `warranted`) is the stream of the new seat `mesh-hand-acts`, held by a new module in mesh-catalog, which every recorder calls from this delivery with no switch; the checker and the assigner's composition check read the held values of their pull requests from mesh-config's state `kept`; from the lease write a trusted config value is set only on the operator's Approve; `settings`, `move-config` and `move-placement` end with the lease and nothing replaces them. each person's six policy choices are set through `mesh-wants.upgrade` and recorded as hand acts, `upgrade compare` and the assigner's finding `policy-split` show any split, and the lease write waits for `compare` to be empty (ADR 0565 §8). One delivery group, built before the lease write of L3, which waits for the group delivered, the six choices set, mesh-config's `compare` empty and its take-over done, `mesh-wants.upgrade compare` empty, and the readings of ADR 0565 §12 (ADR 0565 §14; ADR 0348 §9, ADR 0502 §2, ADR 0539 §14, §15, ADR 0547 §11) | — | mesh-sdk, mesh-catalog (mesh-config, mesh-hand-acts, pacman, the forge module, claude-code, mesh-knowledge, mesh-conditions, mesh-healers), mesh-delivery (indexer, assigner, checker, grants part, forge bridge), the node-engine (`mesh-host`); mesh-controller is not a member | 1953 | | 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..` 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 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-backup` holder, nats: a module's kept data on the bus as a backed-up item), the node-engine (`mesh-host`), hq | 1966 (with the registry milestone's heads), 1961, 1660, 1962, 1972, 1721, 1783, 1785, 1794, 1791, 1373 | 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 @@ -1525,8 +2406,61 @@ the Approve, it is proved again by milestone 3. R4's prepare step on 2026-10-20 cut release 4 by its orders into R4a to R4g, each one release (ADR 0453, the operator's Approve): the SDK ships before its users, the new parts run dark before anything relies on them, the status switch follows a measured run, and the scripts and build-agent go last; R4d, the three image reworks (route-proxy, nats, automx; n8n left by ADR 0506), had no order of its own and was delivered between R4a and R4e. Where a record dates a rule by release 4, it reads the R4 release that builds the rule (ADR 0453 clause 15). +### The bootstrap delivery, and the one milestone after it + +*ADR 0644; ADR 0612 §3, ADR 0603 §10, ADR 0614 §7, ADR 0633 §6, ADR 0636 §4, ADR 0448 §2, §4; research 085, +Direction.* + +Milestone G changes the seat definitions and the checker that checks them, so the running checker could never pass +it, and it could not merge until it passed (ADR 0644, Context). One delivery, B0, goes first, and none after it: from +the milestone's delivery on, nothing of a change is delivered ahead of it. This changes ADR 0612 §3, which keeps every +order inside one milestone, for this one case (ADR 0644). + +- **What it carries.** Only what the running checker must run to check milestone G: the checking program built from a + group's heads, and one seat list handed to the manifest validator and the composer (ADR 0644). The checker builds the + checking program from the members' heads and runs it in the build seat's sandbox through the seat `node-build-go`'s + ask `run`, with the group's heads as its input, no network and no bus account (ADR 0448 §2, §4); the running checker + only asks for the build, reads the program's verdict, records it and publishes it. The seat list is the one ADR 0636 + §4 decides, with its insight of 2026-11-06; B0 builds its hand-over and decides nothing new about it. +- **The trunk's validator still judges inside a group's check.** The indexer's new manifest is also validated with + the trunk's manifest validator, built from the trunk (ADR 0633 §6). +- **The running checker checks it.** B0's group changes no seat definition and no contract, so the running checker + reads every manifest of it; for B0's own group, what the running checker cannot run is still composed from its + manifest and named in the verdict, as ADR 0603 §10 and ADR 0614 §7 have it until milestone G. +- **Its tool.** 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. Asking a check again stays the checker's existing ask-again. +- **What it costs.** One more check, Approve and delivery before milestone G; milestone G then has one check, on its own + checking program (ADR 0644, Consequences), and a group's check costs one more build in the build seat, within the + checker's step bound (ADR 0639 §5). +- **Milestone G keeps the rest**: the first tier for every acting part, the verdict that fails by name, the revision + view, the two graphs, the difference, the acting rules, the delivery record and the trace's links, with the + registry milestone's reviewed heads joined to its group. It is one milestone, one group and one check, its fixtures + folded into the existing whole-module tests of the checker, the gate, the indexer and the assigner, and it ends with + no higher test count in any repository than it starts with (research 085, Direction, One milestone). +- **The order inside milestone G is the module graph's**, never a delivery ahead: each acting part's new version acts + first in its own step (ADR 0656; *Order without a person*); the node-engine handing the fingerprint composes the + modules whose runtime reads it; the indexer's own steps keep *Taken in by the indexer it names*. +- **How it is checked.** Milestone G's group check runs on the checking program built from that group's own heads, + read from the check's record (live; ADR 0644, How it is checked). In the checker's + whole-module test, a fixture group whose checker change alters a rule is judged by its own checking program, which + has no bus account, a fixture group whose heads add a seat has that seat in the list both the validator and the + composer receive, and the check's read names the checking program's build and heads; live, B0's own check passes on + the running checker, naming any module composed from its manifest; whole (gate, assigner), built with milestone G + and run in its check, a group changing the indexer, the checker and a module is delivered with no delivery ahead of + it. + ## What is not decided here +- 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. +- 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. + +- Whether the builder should hold a seat of its own, so that its waits are a seat's events like the other holders': + a later proposal. +- Which manifest grants the node agent the call of a restored part's catch-up tool (noted on issue 1921). - 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. - A web view of deliveries beyond the pull request's comment. @@ -1536,21 +2470,23 @@ R4d, the three image reworks (route-proxy, nats, automx; n8n left by ADR 0506), ## Reviewed by architectural domain -The amendment of 2026-11-05 (ADR 0637, ADR 0638, and ADR 0633 with its insights of 2026-11-05) was reviewed by the -four reviewers by architectural domain side by side (playbook 01), and the lead ruled on every finding in round 1. -The rulings that no record decided became ADR 0639, which the text cites clause by clause. What was found, and what -answers it: +The amendment for ADR 0656 and ADR 0644 (2026-11-06) was reviewed by the four reviewers by architectural domain side +by side (playbook 01), in three rounds, the lead ruling on every finding; a fourth, confirming read covered only the +revert section, written after the operator's ruling that no old form is kept. Every choice it answers with is this +design's own under ADR 0656, written with its reason in *Choices this design makes under ADR 0656*. What was found, +and what answers it: | Domain | Found | Answered by | |---|---|---| -| seams | the indexer said to read none of the checker's state, while it reads the checker's previews and the checker watches its candidates; the forge bridge's read of the gate's `merges` unmarked | *A try* (two reads that stay under ADR 0596 §3 until issue 1966); *Taken in by the indexer it names* (the bridge's read marked the same) | -| tools | the try's tools not listed against the four lines; the hand acts not said to clear the condition | *A try* (the table of the checker's and the indexer's tools, `indexer_recover_tries` new); *Taken in* (both hand acts emit `take-in.taken`, ADR 0639 §9) | -| bus forms | the form of `pending` and the try events' contract unsaid | *Taken in* (`pending` in the old form the grants part creates); *A try* (the package `tryevent` of mesh-delivery's sdk) | -| running qualities | `checker_tries` not answering; the new indexer before the new checker in this release; a lost try event; what a narrowed try keeps; a recovery cut at 64; how long tries are kept; a try waiting for its candidate | *A try* (nothing deleted, asked again, said in `indexer_try_candidates`, the ordering edge; publish with retry, `try.ended` on every end; the narrowed try's verdicts; every open try listed; 7 days; the reason in the try read, bounded by the step bound) | -| testing | ADR 0633's live case on the node-engines' reads lost; no live case for ADR 0638 | *Taken in* (`node_manifest_reads` and `indexer_holds` on every node); *A try* (the candidate read during the registry group's try, gone after `try.ended`) | -| health and events | an own build unknown left unbounded; who raises `take-in-waits`; what the two take-in events carry | *Taken in* (`take-in.pending` "own build unknown"; the watchman's finding and the condition keeper, ADR 0564 §2; the events' body) | -| security | any account could publish a try event; a node's missing composition answer | *A try* (only the checker's account publishes `try.*`, a manifest-test row; the try ends as error naming the node, keeping no verdict) | -| upgrade | the old indexer's take-in during its replacement: a build learnt lazily and kept let a process being replaced take in with its old validator | *Taken in* (the indexer fixes its own build at its start, never later from `composition`) | +| seams | a capability change read from the node-engine's bucket; a live search that could not pass; the node-engine's input said two ways; waits held by four parts but read and released by one; a new field in the want an old node-engine cannot read; the catch-up tool named by a manifest a revert cannot carry | `capabilities-changed`; the search narrowed to the reads the view replaces; the node-engine reads its want and the catalog half; each holder's own wait tools; the assigner holds a node's wants until its new node-engine runs; the restore point carries the catch-up tool | +| tools | no fill read; the repair tool's path; the indexer's hand tool; the data set aside at a restore | fill in each read tool; `gate_record_repair` names each owner's verb; `indexer_recompute`; `backed-up` lists it and `discard-aside` removes it | +| running qualities | storage that grows without limit; the build module's wait ended two ways; a checking program built per check; the wait clocks; a revert whose assigner starts before its indexer catches up | 90-day records, unpinned revisions removed, entries stored once, caps with `filling`, `full` and `room`; the agreed toolchain; one build per fingerprint; clocks from the build's end, composing per node; the ordering edges of a revert | +| testing | a revert test that needed a second check; the view with the assigner down | whole-module revert tests on the real builds from before milestone G; the assigner stopped and a cold control-node | +| health and events | waits with no event or bound; a record that never ends; parts waiting for the view unreported; when a wait is first said | `wait-held` at the start with its bound, timed by the watchman; held and failed outcomes and `record-ended`; `view-waits` after five minutes | +| security | a checking program hiding a removed rule; forged events; the `run` ask's grant; a kept build overwritten by the program it built; the review line retired before the review verb's grant | the running checker names every change of the checking program's fingerprint; the node in event subjects and grants per account; builds kept only from a step that runs nothing; the review verb left to issue 1782 | +| upgrade | a newer indexer never wanted after milestone G; a revert across a change of kept data's form | the stand-in catalog graph revision; restore points taken before a move across a `form` change, restored at a revert before the reverted build starts, the node-engine back last | -Left as notes, not in this amendment: the grants of `checker_try` and of the hand tools (the operator's), and the -test budget of the checker and the indexer. +Left as notes on issue 1921, not in this amendment: a conformance check that a changed layout raises its `form`; the +size and retention of restore points and set-aside data; who may call `restore` for bus data and which manifest grants +the catch-up call; the hold's bound while a large backup is taken; the granting and placing bound read from the first +live run; a builder seat.