From 7f2f37d9e6d7ea7fbe2c4700d25e5a1e55102442 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 13:01:37 +0100 Subject: [PATCH 1/8] Amend design 47 for milestone G's prepare, so G is built from what the records hold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prepare found the design behind its records and the code: Placement retired by ADR 0641 while the design still put two seats in a contracts module never made, ADR 0659 and ADR 0660 not followed, the checker and the forge bridge reading the gate's state, the delivery parts naming the forge module's tools, and four hand-wired seat-list readers. Design 47 now writes each as the design's choice under the record it follows; the hold and the merge turns are removed by the operator's word with their record still to come, and a session's recovery merge waits for one. The glossary moves Placement's words into Core and Delivery and adds the seat mesh-grants; the words test refuses Placement as a domain heading (ADR 0641 §5); design 26 takes the new contracts homes and the seat git's verbs. Claude-Session: https://claude.ai/code/session_01NcbgfFfQ4Z9UE5jK32PmE5 --- 00-META/checks/words_test.go | 30 ++ 00-META/glossary.md | 86 ++--- 03-DESIGN/01-to-be/26-the-seats.md | 21 +- .../47-delivery-from-commit-to-delivered.md | 340 ++++++++++++++---- 4 files changed, 369 insertions(+), 108 deletions(-) diff --git a/00-META/checks/words_test.go b/00-META/checks/words_test.go index 3707e7f6..910b670d 100644 --- a/00-META/checks/words_test.go +++ b/00-META/checks/words_test.go @@ -38,6 +38,7 @@ import ( // holders the glossary says no seat's holder: no seat "held by" or "claimed by" a named module, no named module // "holder of" a seat, no module that "holds the seat" (the operator, 2026-11-09). Who holds a seat is read // from the mesh's assignments, and the glossary's head says where. +// domains no retired domain heads a section of the glossary (ADR 0641 §5: Placement). // copy with MESH_CATALOG_DIR set to a checkout of the catalog, its copies of the retired words and the // daemons' lines equal this glossary's and this list (skipped, with its reason, when unset). // @@ -830,3 +831,32 @@ func TestHoldersRefusesANamedHolderAndPassesARole(t *testing.T) { } } } + +// domains: a retired domain is no heading of the glossary. ADR 0641 §1 retired Placement into Core (module +// assignment) and Delivery (the wants), and §5 has this test refuse it as a domain heading. + +var retiredDomains = []string{"Placement"} + +func checkRetiredDomains(text string) []string { + var out []string + for _, d := range retiredDomains { + rx := regexp.MustCompile(`(?m)^##\s+` + regexp.QuoteMeta(d) + `\b`) + for _, loc := range rx.FindAllStringIndex(text, -1) { + out = append(out, fmt.Sprintf("%s:%d: %q is a retired domain (ADR 0641 §1) and heads no section", glossaryFile, lineOf(text, loc[0]), d)) + } + } + return out +} + +func TestWordsGlossaryHasNoRetiredDomainHeading(t *testing.T) { + expectNone(t, checkRetiredDomains(readFile(rootFS(), glossaryFile))) +} + +func TestRetiredDomainsRefusesTheHeadingAndPassesProse(t *testing.T) { + if len(checkRetiredDomains("## Placement — what runs on which node\n")) == 0 { + t.Error("a Placement heading was not refused") + } + if got := checkRetiredDomains("## Core\n\nwhich was the Placement domain until ADR 0641\n"); len(got) != 0 { + t.Errorf("prose refused: %v", got) + } +} diff --git a/00-META/glossary.md b/00-META/glossary.md index 1a207f22..3510aea2 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -165,8 +165,8 @@ and repair). **Purpose.** To hold, check and resolve every value a module reads that someone may change, and to say where each resolved value came from. **Recorded by** the `mesh-config` module, in its buckets on the bus, with its store as the history behind them. **Upstream of** Provisioning, which places the values; -downstream of Module and Placement. **Decided by** ADR 0262, 0277, 0348. -**Uses:** module, node, manifest (Module), assignment (Placement), provision (Provisioning), proposal +downstream of Module and Core. **Decided by** ADR 0262, 0277, 0348. +**Uses:** module, node, manifest (Module), assignment (Core), provision (Provisioning), proposal (Operator and conversation). - **template file** — a file a module ships with placeholders, such as `${config:}`, which the provisioner @@ -225,10 +225,13 @@ downstream of Module and Placement. **Decided by** ADR 0262, 0277, 0348. ## Core — the mesh's own machinery **Purpose.** To keep the controller, the bus, the store and every node's engine running and agreed, so -every other domain has something to run on. **Recorded by** the controller and the store. -**Upstream of** every domain but Module; the others use the bus and the store as they are. -**Decided by** ADR 0005, 0006, 0067, 0106, 0141, 0175, 0227, 0229, 0289. -**Uses:** node, module, seat. +every other domain has something to run on; and to decide and apply what each node runs, and say why: module +assignment, which was the Placement domain until ADR 0641 §1 retired it into this one. **Recorded by** the +controller and the store; for module assignment, by the seat `mesh-module-assigner`, in its states on the bus +(ADR 0567). **Upstream of** every domain but Module; the others use the bus and the store as they are. +**Decided by** ADR 0005, 0006, 0067, 0100, 0106, 0126, 0141, 0175, 0176, 0181, 0207, 0221, 0227, 0229, 0252, 0261, +0289, 0411, 0539, 0549, 0559, 0565, 0567, 0571, 0634, 0641. +**Uses:** node, machine, module, seat, manifest (Module), config value (Config), proposal (Operator and conversation). - **controller** — the component that decides what each node should be, holds the mesh's records, and tells nodes over the bus. The relationship is *controller and nodes*, and no node is subordinate: a @@ -299,22 +302,8 @@ every other domain has something to run on. **Recorded by** the controller and t text is about the controller's records; the builder's *mirrored* `upstream` image (Delivery) is a different thing, a copy of an outside image in the artifact store. -## Placement — what runs on which node - -**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, 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 @@ -323,11 +312,8 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02 - **mesh-module-assigner** — the seat, held once for the mesh, that keeps placement: its states `placement`, `proposals` and `candidates`, and its seven verbs `placement`, `proposals`, `propose`, `withdraw`, `again`, `adopt` and `hand-back`. Only the holder writes its states (ADR 0567, renamed by ADR 0634 §9). Its - contract is in `mesh-placement-contracts` (ADR 0634 §7). + contract is in `mesh-contracts`, module assignment being of this domain (ADR 0641 §1). *Not:* ~~mesh-placement~~ -- **mesh-wants** — the seat, held once for the mesh, that keeps what each node should run, one want - per node and module (`want..`), and a person's upgrade policy, served as its verb `upgrade` - (ADR 0565 §7, ADR 0571 §8). Its contract is in `mesh-placement-contracts` (ADR 0634 §6, §7). - **seat holders** — the state `seat-holders` of the seat `mesh-module-assigner`, written by its holder: one key per seat naming each holder and its node, composed from placement and the manifests' claims as the indexer holds them, and rewritten only when a key differs. The tool runner and every other reader of who holds a seat @@ -409,9 +395,9 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02 **Purpose.** To resolve which module serves a provision for which consumer, to wire the two, and to place every value a module needs into its files on its node. **Recorded by** the controller and -`mesh-provisioner`. **Upstream of** Data; downstream of Module, Placement, Config and Identity. +`mesh-provisioner`. **Upstream of** Data; downstream of Module, Core, Config and Identity. **Decided by** ADR 0027, 0084, 0138, 0225, 0230, 0263, 0349. -**Uses:** module, seat, node-engine (Core), declaration (Placement), config value (Config), credential +**Uses:** module, seat, node-engine (Core), declaration (Core), config value (Config), credential and vault (Identity and access). - **provision** — a service one module `provides` and others `require`; the mesh resolves a provider @@ -467,7 +453,7 @@ and vault (Identity and access). **Purpose.** To issue, hold, rotate and check the credentials and grants by which modules, nodes, agents and the operator act. **Recorded by** the controller's `identity` and `licences` databases and the vault. **Upstream of** Provisioning, Delivery, and Operator and conversation. -**Decided by** ADR 0085, 0113, 0183, 0225, 0234, 0386, 0387, 0388, 0411, 0413, 0414, 0550, 0571, 0587, 0634. +**Decided by** ADR 0085, 0113, 0183, 0225, 0234, 0386, 0387, 0388, 0411, 0413, 0414, 0550, 0571, 0587, 0634, 0636, 0656, 0660. **Uses:** module, node, seat, tool (Module), operator, consumer and provision (Provisioning), agent and ask (Operator and conversation). - **credential** — anything that proves an identity: a password, a key, a token. A **pair credential** @@ -514,6 +500,11 @@ and the operator act. **Recorded by** the controller's `identity` and `licences` is issued. The bus server calls its accounts *users*; the mesh says bus account. Each module's membership is kept in the grants part's state `memberships` (seat `mesh-grants`), one key `.` written only when it differs, and the runtime reads its membership there (ADR 0571 §10). +- **mesh-grants** — the seat, held once for the mesh, that composes who may say what on the bus: each module's + membership, the bus's user list and `grants..`, applying mesh-sdk's `SeatsToPassOn` for a + seat's pass (ADR 0660 §3). From research 085's milestone it is defined whole in `mesh-identity-and-access-contracts`, + this domain's contracts module (ADR 0636 §5), as an acting part of the step `grant` (ADR 0656 §1), with the events + `wait-held` and `wait-ended` (design 47). - **rotate** — replacing a credential without a consumer holding one the provider does not know. - **licence** — a model-access account the licence manager holds and **binds** to a consumer, the coding agent on a node (ADR 0183). @@ -535,8 +526,8 @@ 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, 0644, 0656. -**Uses:** module, node, declaration and send (Placement), health (Health and repair), person. +**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, 0641, 0644, 0656, 0659. +**Uses:** module, node, declaration and send (Core), health (Health and repair), person. - **merge check** — what a pull request carries before it may merge, judging the merge of its head into a named **trunk commit**, made in a clone of its own on a build node and never pushed (ADR 0323). One status, @@ -550,8 +541,18 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel 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. + its step (ADR 0656). Nothing stores the order. Not the **assignment graph**, computed from it. *Not:* ~~build graph~~ +- **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**, whose entries are modules, not + versions on nodes; not an **assignment**, one module put on one node. +- **mesh-wants** — the seat, held once for the mesh, that keeps what each node should run, one want + per node and module (`want..`), and a person's upgrade policy, served as its verb `upgrade` + (ADR 0565 §7, ADR 0571 §8). Of this domain: the wants are what the delivery asks of the nodes, so its contract is in + `mesh-delivery-contracts` (ADR 0641 §2). - **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 @@ -600,7 +601,9 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel to merge there in the order of their Approve, and the check each waits for. It holds new checks off that repository until the merge is done (ADR 0548 §3). Written only while the check gates merges (the gate's setting `check-gates-merge`); while it does not, no turn is written and an approved merge goes in at once - (ADR 0551 §1). + (ADR 0551 §1). Goes at research 085's milestone with the hold and the setting, once the record that ends them is + accepted (the operator, 2026-11-09; design 47); the word goes to a *Not:* line with the as-is update after that + delivery. - **build seat** — a language's node seat, held on a node by that language's build module: `node-build-go`, `node-build-node` and `node-build-image` (ADR 0525 §2). Its accepted kinds are the **build streams**, and its verbs `current`, `pause` and `resume` serve that node's pulls. It replaces the seat `node-build-agent`, retired @@ -809,7 +812,10 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel nodes; what a lab run declares; and a recorded failure run again to prove a fix (ADR 0016, ADR 0237). - **git seat** — the seat `git`, held once for the mesh, delivering the provision `git`: the repositories a build comes from ([26 — The seats](../03-DESIGN/01-to-be/26-the-seats.md) *The git seat*). A seat of - this domain. + this domain, defined in `mesh-delivery-contracts`. From research 085's milestone its verbs are the one way a + delivery part reaches the forge: reads (`pull`, `open-pulls`, `tags`, `trees`, `file`, `commits`, `branches`, + `contains`) for the parts that declare them, and acts (`status`, `comment`, `view`, `note`, `merge-pull`) for the + forge bridge alone (design 47, *The verbs*). Not the **forge** itself, the server, nor the forge module's own tools. ## Health and repair — what is wrong, and putting it right @@ -1254,18 +1260,18 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical. | Word | Sense | Say | Domain | |---|---|---|---| -| plan | what the controller would send one node, until release 6 | that node's **declaration** (verb `plan`) | Placement | +| plan | what the controller would send one node, until release 6 | that node's **declaration** (verb `plan`) | Core | | | what a delivery would do to the mesh, until release 6 | **delivery plan** | Delivery | | | the sending of one batch of commits across nodes, until release 6 | **rollout** (verb `plans`) | Delivery | | | a step a person starts, like the bus's | **planned step** | Core | -| composition | one module's resources for one node at one fingerprint, signed, from release 6 | **composition** | Placement | -| | the function in `mesh-delivery-sdk` that composes them, which the node agents, the assigner and the grants part run (ADR 0635 §4) | **the composition of `mesh-delivery-sdk`** | Placement | +| composition | one module's resources for one node at one fingerprint, signed, from release 6 | **composition** | Core | +| | the function in `mesh-delivery-sdk` that composes them, which the node agents, the assigner and the grants part run (ADR 0635 §4) | **the composition of `mesh-delivery-sdk`** | Core | | | whether the machines a change touches still compose | **composition check** | Delivery | | membership | the subjects a bus account is issued | **membership** | Provisioning | | binding | the names on the bus of a module's bus state, in its membership | **binding** of bus state | Data | | | a consumer's tie to the data a provider keeps for it | **sticky binding** | Data | -| | which host ports and addresses a placement gives a module | a placement's **bindings** | Placement | -| | an account in a group of the machine | **account group** | Placement | +| | which host ports and addresses a placement gives a module | a placement's **bindings** | Core | +| | an account in a group of the machine | **account group** | Core | | domain | an area of the mesh that owns a set of concepts and the one word for each (ADR 0244), the sections of this page; the one sense "domain" keeps bare | **mesh domain** | The record | | | one of the eight concerns a direction and a design are reviewed against (playbook 01); never written bare | **architectural domain** | The record | | configuration | the settings of a storage type, which a template fixes or a module gives | **configuration** of a storage type | Data | @@ -1278,7 +1284,7 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical. | | one choice of an ask to a person | an ask's **option** | Operator and conversation | | effect | what an operation does to a ledger's state | **ledger effect** | Data | | | a tool's mark: reads, acts or dangerous | a tool's **effect** | Identity and access | -| push | the controller giving nodes their declarations, until release 6 | **send** (verb `push`) | Placement | +| push | the controller giving nodes their declarations, until release 6 | **send** (verb `push`) | Core | | | a git push | **git push** | The record | | | a phone notification | **push notification** | Operator and conversation | | release | a person letting a held delivery go on | **release** | Delivery | @@ -1288,7 +1294,7 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical. | gate | the judgement on a delivery's first node | **first-node gate** | Delivery | | | the check of what a change makes of the machines it touches, a pull request status until release 4 of ADR 0429 | ~~merge gate~~ (hq); the **composition check** | Delivery | | | the part that merges a group and moves the source revision | **gate part** (`mesh-delivery-gate`) | Delivery | -| | a failed step stopping its module's later steps (ADR 0136) | *a failed step holds its module* | Placement | +| | a failed step stopping its module's later steps (ADR 0136) | *a failed step holds its module* | Core | | tier | a level of the mesh | **layer** | Core | | | a step of a rollout | **tier** | Delivery | | | how much proof an ask needs | **assurance level** | Identity and access | @@ -1307,7 +1313,7 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical. | | an entry in the build queue | **build request** (an ask on a build stream) | Delivery | | store | the one database server | **store** | Core | | | the OCI registry | **artifact store** | Delivery | -| | a node's own copy of its last declaration | **last declaration** | Placement | +| | a node's own copy of its last declaration | **last declaration** | Core | | | the seat holding the mail accounts | **`mail-store`** | Mail | | record | a numbered decision | **decision record** | The record | | | the literal reader of this repository, searched first for a symptom | **the record** (the `records` module) | Operator and conversation | diff --git a/03-DESIGN/01-to-be/26-the-seats.md b/03-DESIGN/01-to-be/26-the-seats.md index 6fb58ecc..fc9547b1 100644 --- a/03-DESIGN/01-to-be/26-the-seats.md +++ b/03-DESIGN/01-to-be/26-the-seats.md @@ -15,8 +15,9 @@ code: - mesh-catalog modules/gitea/module.json - mesh-controller internal/inventory/migrations/0063-a-machines-names-are-one-seats.sql - mesh-controller internal/inventory/migrations/0064-the-resolver-file-is-the-uplinks.sql -updated: 2026-11-06 +updated: 2026-11-09 decisions: + - "ADR 0641" - "ADR 0636" - "ADR 0634" - "ADR 0635" @@ -255,14 +256,21 @@ defined whole or at the seat list's grain (ADR 0635 §2). In the milestone of AD | contracts module | domain | seats | held by | |---|---|---|---| - | `mesh-delivery-contracts` | Delivery | `mesh-catalog`, `mesh-delivery-checker`, `mesh-delivery`, `node-build-go`, `node-build-image`, `node-build-node`, `npm-package-registry`, `go-package-registry`, `container-image-registry`, `oci-artifact-registry` | the indexer, the checker, the gate part, the three build modules, the forge (two), `distribution` (two) | - | `mesh-placement-contracts` | Placement | `mesh-module-assigner`, `mesh-wants` | the assigner | + | `mesh-delivery-contracts` | Delivery | `mesh-catalog`, `mesh-delivery-checker`, `mesh-delivery`, `mesh-wants`, `node-build-go`, `node-build-image`, `node-build-node`, `npm-package-registry`, `go-package-registry`, `container-image-registry`, `oci-artifact-registry` | the indexer, the checker, the gate part, the assigner, the three build modules, the forge (two), `distribution` (two) | | `mesh-health-and-repair-contracts` | Health and repair | `mesh-conditions`, `mesh-self-check`, `mesh-healers`, `mesh-watchdogs`, `mesh-hand-acts` | the condition keeper, mesh-self-check, mesh-healers, the watchman, mesh-hand-acts | | `mesh-provisioning-contracts` | Provisioning | `mesh-provisioner` | mesh-provisioner | | `mesh-identity-and-access-contracts` | Identity and access | `mesh-vault`, `mesh-node-enroller` | the vault, the module `mesh-enrolment` | | `mesh-connectivity-contracts` (mesh-networking repository, ADR 0636 §6) | Connectivity | `the-private-network` | `mesh-wireguard` | | `mesh-operator-and-conversation-contracts` | Operator and conversation | `operator-channel` | the router | - | `mesh-contracts` | Core, and Config | `node-engine`, `mesh-broker`, `mesh-config` | the node-engine, the bus module, mesh-config | + | `mesh-contracts` | Core, and Config | `node-engine`, `mesh-broker`, `mesh-config`, `mesh-module-assigner` | the node-engine, the bus module, mesh-config, the assigner | + + ADR 0641 retired the Placement domain: module assignment is Core's, so `mesh-module-assigner` is defined in + `mesh-contracts`, and the wants are Delivery's, so `mesh-wants` is defined in `mesh-delivery-contracts`; the module + `mesh-placement-contracts` is not created (ADR 0641 §1, §2). From research 085's milestone (design 47) the seat + `mesh-grants` is defined whole in `mesh-identity-and-access-contracts` (ADR 0636 §5), and the seat `git` gains in + `mesh-delivery-contracts` the verbs by which every delivery part reaches the forge (*The git seat*, below). How it is + checked: the indexer refuses a definition of these seats from any other contracts module, naming both (ADR 0636 §5), + and its domain check refuses a definition naming the domain Placement (ADR 0641 §5). **How it is checked**: each of these definitions' conformance fixtures names every event's subject, the seat's and the holder's own, and a case of the holder's whole-module test takes a fixture from the running holder (an event @@ -681,6 +689,11 @@ difference: it receives a URL either way. With the seat unheld, a build from the seat is refused and says why. External builds carry on. +**The seat's verbs, from research 085's milestone.** The delivery parts reach the forge through the seat `git`'s +verbs alone, never by the forge module's own tools: reads for the parts that declare them in their `invokes`, and +acts (a status, a comment, the delivery's view, a line on the commit's note, a merge) for the forge bridge alone. +Design 47, *The verbs*, lists them with their callers and how they are checked. + **Not yet designed:** a credential for cloning a private repository. The mesh's own repositories are public. The natural place for a clone credential is a `secret` from the vault, and that is a decision still to take. 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 2838593a..bdbb118a 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,11 +1,14 @@ --- layer: to-be status: in-progress -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 +code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-catalog modules/gitea, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, mesh-contracts, hq 00-META/checks] +updated: 2026-11-09 decisions: + - "ADR 0660" + - "ADR 0659" - "ADR 0656" - "ADR 0644" + - "ADR 0641" - "ADR 0639" - "ADR 0638" - "ADR 0637" @@ -14,6 +17,7 @@ decisions: - "ADR 0634" - "ADR 0633" - "ADR 0632" + - "ADR 0625" - "ADR 0619" - "ADR 0618" - "ADR 0615" @@ -140,7 +144,7 @@ release 6 each node takes a merged change from its wants, the module's canary fi milestones are features delivered one after another. The trunk is what runs; there is no "release branch"** (ADR 0401, ADR 0418 §1, ADR 0421 §2). -**From the delivery of issues 1781 to 1783, a milestone is a record of the issue tracker and the unit that is released.** Its issues are linked to it, and pull requests and commits to the issues, by the issue tracker's link verb, never matched from a description; a review is recorded by a verb of the delivery seat; the mesh builds and checks each pull request as it comes in, and asks the operator once to release the milestone when every issue is ready; its pull requests are the delivery group, in whatever repositories and on whatever branches, and nothing ships outside a released milestone (ADR 0493 §1 to §11). Until that delivery the branch-name rules in this design stay what runs, kept by hand (ADR 0493 §12). A change across any repositories, the mesh-sdk included, is one group whose developer orders no release, from R4e's delivery for a group with a mesh-sdk member; until then an SDK change ships before its users (ADR 0482 §1, §7). +**From the delivery of issues 1781 to 1783, a milestone is a record of the issue tracker and the unit that is released.** Its issues are linked to it, and pull requests and commits to the issues, by the issue tracker's link verb, never matched from a description; no part reads a pull request's description to hold, allow or withdraw an ask or a merge, and a review, if a verb records it, gates nothing (ADR 0659 §1, §2); the mesh builds and checks each pull request as it comes in, and asks the operator once to release the milestone when every issue is ready; its pull requests are the delivery group, in whatever repositories and on whatever branches, and nothing ships outside a released milestone (ADR 0493 §1 to §11). Until that delivery the branch-name rules in this design stay what runs, kept by hand (ADR 0493 §12). A change across any repositories, the mesh-sdk included, is one group whose developer orders no release, from R4e's delivery for a group with a mesh-sdk member; until then an SDK change ships before its users (ADR 0482 §1, §7). **Every step starts on an event, and the state lives on the bus.** The forge's events, the parts' keys and the nodes' haves move a delivery; nothing collects merges, plans them or orders them in tiers. From release 6 each @@ -178,8 +182,10 @@ no part calls another, there is no plan object and no orchestrator, and each key source revision is derived from the keys: every **want** equals its module's **fingerprint**, and every **have** equals its want and is healthy. -- **The forge bridge** turns the forge's events into stream messages and is the only part that calls the - forge. +- **The forge bridge** turns the forge's events into stream messages and is the only part that acts on the + forge. From research 085's milestone every delivery part reaches the forge through the seat `git`'s verbs alone, + never by the forge module's own tools: the forge bridge for every act and read it makes, the checker and the + indexer for the reads they make (*The seat `git`*, under *The verbs*). - **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). From research 085's milestone it keeps the catalog graph per @@ -202,12 +208,13 @@ source revision is derived from the keys: every **want** equals its module's **f 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). 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 gate** merges a group's pull requests once the check and the operator's Approve agree on the same + heads, reading no review line (ADR 0659 §1), 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); research 085's + milestone removes the hold, the turns and the setting (*Nothing merges into a repository under check*, below). From + that milestone it keeps one delivery record per milestone's delivery as a ledger instance (ADR 0656), its `merges` + 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` @@ -238,7 +245,9 @@ source revision is derived from the keys: every **want** equals its module's **f §3; ADR 0539 §16). Nothing composed crosses the bus, so nothing is signed; of other nodes it reads only their public records and the manifests wanted on them. On the node holding `mesh-broker` it also writes the bus's user list, alone and with no want (*The bus's user list*, below). -- **The grants part** (module `mesh-grants`, holding the seat `mesh-grants`) composes who may say what on the bus +- **The grants part** (module `mesh-grants`; from research 085's milestone it claims the seat `mesh-grants`, defined + whole in `mesh-identity-and-access-contracts`, its domain's contracts module, ADR 0636 §5, as an acting part of the + step `grant`, ADR 0656 §1) composes who may say what on the bus with mesh-sdk's bus composer, whose parts that name a seat or a holder's state are the grants part's own package (ADR 0635 §4): each module's membership, the buckets, logs and streams a manifest declares, the bus's user list, and `grants..`, written from L7's delivery once the list the bus loaded @@ -341,7 +350,9 @@ The target (ADR 0547): - **The forge module** (`gitea`) publishes every forge event on the bus through the forge's system webhook (issue 729): pull requests updated, synchronized, merged and closed, and commit statuses; no label or comment moves a delivery, and the gate subscribes to none (ADR 0421 §11). It sets statuses, keeps the delivery's view - as one comment on the pull request, and appends to the commit's note under `refs/notes/mesh-plan`. + as one comment on the pull request, and appends to the commit's note under `refs/notes/mesh-plan`; from research + 085's milestone it does these, and answers every read a delivery part makes of the forge, as the verbs of the seat + `git` it claims (*The verbs*). - **Each node's backup seat** (`node-backup`) backs up the data a module keeps on its node before the node agent applies a new fingerprint of it, and says on the bus when that backup ended (ADR 0404, ADR 0539 §16). @@ -381,8 +392,8 @@ Its id is `/@`. The gate hol - the pull request (number, base, head branch, description), or none for a commit that reached the trunk without one; - its **state** and its **transitions** (the last hundred, each with when, the event, from, to and why); -- its **Approve**: the ask, the operator's answer with its warrant, and the review record the pull request was - opened with (ADR 0421 §5, §8); +- its **Approve**: the ask and the operator's answer with its warrant (ADR 0421 §8); the ask carries no review + record (ADR 0659 §1); - once merged, **the commit it landed on the trunk as** and the source revision its merge moved. Every member of a delivery group moves the same source revision (ADR 0429 §11). @@ -415,7 +426,7 @@ From research 085's milestone the gate, holder of the seat `mesh-delivery`, keep 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 group's state**: the heads, the verdicts, the checks and the asks, which `merges` holds 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, @@ -466,8 +477,9 @@ ADR 0656. 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 +- **The switch carries today's state in, once.** At the new gate's start, `merges` and every group in flight become + records, and from then on the gate writes `merges` no more; `turns`, which nothing writes while the check gates no + merge, is dropped with the hold (*Nothing merges into a repository under check*). 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 @@ -475,14 +487,36 @@ ADR 0656. - **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`. +- **What the gate owes the forge is said, not watched.** The forge bridge today performs what the gate's states + `merges` and `owed` ask (a merge, a status, the delivery's view, a comment, a line on the commit's note) by watching + those keys (ADR 0437 §8), a read of another module's bus state ADR 0596 §2 ends when the gate moves. From research + 085's milestone the gate says each act it owes as the event `owed` of the seat `mesh-delivery`, carrying the act, + its pull request and head, and an id; only the gate's account publishes it (ADR 0363 §2). The forge bridge hears it + through a durable consumer declared in its `consumes`, performs each id once, keeping which ids it performed in its + own state, and does it through the seat `git`'s verbs. The verb `groups` also answers, per group, the acts the gate + said with their ids, and the bridge's tool `bridge_owed` the ids it performed and when, so a person reads both sides + through their owners and nothing reads the other's state. This + is the design's choice under ADR 0596 §1, §2 and ADR 0656 §7: an event consumed durably loses no act while the + bridge is down and performs none twice, and one verb answers a group and what it owes (lead-47, 2026-11-09: no + second verb). How it is checked: in the forge bridge's whole-module test an `owed` said while it is stopped is + performed once after its restart, an act said twice is performed once, its account cannot read `owed`, and + `bridge_owed` lists what it performed; in the gate's, `groups` answers each act it said for a group, with its id. +- **A merge made at the forge has a record too.** A merge the forge reports that the gate did not make (a person's + merge in the forge's own page, as on 2026-11-07 and 11-09) is taken in like any other merge (*Taken in by the + indexer it names*), and the gate opens a delivery record for it on the forge bridge's merged message, marked + `forge-made`, with no Approve, so its trace and its outcomes on the nodes are kept like every delivery's and no + later merge waits behind a merge the gate never heard. The design's choice under ADR 0656 §7 and ADR 0633 §1 (the + operator, 1921's note of 2026-11-07: "a merge made in the forge is taken in like any other"). How it is checked: in + the gate's whole-module test a merged message with no record of the gate's opens a `forge-made` record, and a later + merge in the same repository is taken in behind it. - **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) | + | 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 verb `haves` 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 | + | one seat, many tools | the seat `mesh-delivery`'s verbs, with `groups` answering a group's members and the acts said for it | - **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 @@ -512,7 +546,7 @@ state has a **bound**: how long it may be held before `stalled` lists it, for a | proposed | a head was announced and is being checked | 1 hour from when its check is asked; none while its verdict is out of date and no check is asked | | ready | it passed; its Approve is asked (a group member's once the group is ready), and it waits for it and the merge | none | | rejected | it failed its check | none | -| held | it waits for a person, a carry or a check: an approved merge whose verdict is being carried to the trunk's head, a conflict, a member the forge refused, and, while the check gates merges, an approved merge waiting on its merge turn for another delivery's running check (ADR 0548 §2, §8, ADR 0551 §1) | 1 day; a merge turn 30 minutes | +| held | it waits for a person, a carry or a check: an approved merge whose verdict is being carried to the trunk's head, a conflict, a member the forge refused, and, until research 085's milestone and while the check gates merges, an approved merge waiting on its merge turn for another delivery's running check (ADR 0548 §2, §8, ADR 0551 §1) | 1 day; a merge turn 30 minutes, until that milestone | | merged | merged on its trunk; its source revision moved | final | | closed | closed without a merge | final | @@ -686,11 +720,28 @@ Findings 11). How each is kept, read and retained is this design's choice under 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). +- **The trunk's heads come from the catalog half.** The checker takes the trunk heads a check merges onto from the + catalog half, which carries the heads, and never reads the gate's state `source` (ADR 0596 §2); with the hold and + the turns removed it reads nothing of the gate at all (*Nothing merges into a repository under check*). +- **The indexer reads every definition before it judges.** At its start the indexer reads the seat definitions of + every contracts module's trunk through the forge (the seat `git`'s verbs) before it judges a manifest or answers + `seat-definitions`, and only then follows the forge bridge's stream; a definition merged while it was down is + therefore never judged against a list that lacks it. The design's choice under ADR 0636 §1 and §4, as the operator + asked it (1921's note of 2026-11-07: "the indexer reads its seat definitions at start"). How it is checked: in the + indexer's whole-module test, started with a definition on a contracts trunk that no event announced, the first + manifest claiming that seat is judged with it. +- **Who holds a seat is applied by one rule, from what each node runs.** The node-engine and the grants part decide a + seat's pass by mesh-sdk's `SeatsToPassOn` (ADR 0660 §3), reading the manifest of the build each assignment runs + (ADR 0660 §2). The build another node runs is read through the node-engine seat's verb `haves` of that node, never + its have key, its want or the assignment half (ADR 0596 §2; *Each part acts on its own entries*). The verb is the + design's choice under ADR 0660 §2: the node that runs a build is the one owner of what it runs. How it is checked: + in the node-engine's and the grants part's whole-module tests a pass is decided from a stand-in seat answering + `haves`, and neither part's account can read another node's have key. - **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 + for the catalog half as ADR 0636 §1 already has it wait for the seat list. Each answers in its runtime's first-view + tool (below) the revision it holds, the one 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 @@ -718,7 +769,18 @@ ADR 0656 leaves how each part is built to this design, under issue 1921; these f (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 same delivery. The bootstrapped definitions every reader carries from its start (ADR 0636 §2), four of which + gain `acts` (`mesh-catalog`, `mesh-delivery-checker`, `node-engine`, `node-backup`), are made again from the new + definitions in the same change, and mesh-sdk's existing test that each bootstrapped copy equals its definition holds + them to it. +- **One wiring of the seat list's reader.** The reader of the seat list and its two tools of ADR 0636 §7 are wired once, + by mesh-sdk's runtime (`go/seatlist`), for every part that reads the list; no part wires its own. Four delivery + parts (the assigner, the checker, the indexer and the grants part) each carry a hand-wired copy today, each with its + own pair of tools, and each copy would have to learn `acts` apart. The design's choice under ADR 0636 §7, which puts + the tools in the runtime bundle: one wiring learns `acts` once, and every part's two tools are the same, named by + their module, with the same callers (any person and the healers to read, the operator and the healers to read + again), the node-engine's among them. How it is checked: mesh-sdk's seat-list test registers both tools for a + fixture part; a search of the delivery parts' code finds no wiring of its own. - **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 @@ -794,7 +856,15 @@ ADR 0656 decides the direction and leaves every choice made in working it out to | 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 | +| The view's state read and asked again by mesh-sdk's `go/views` tools `_first_views` and `_first_view_again`, which gain the revision a part holds and what it waits for; the typed first view and the following of each half in the seats' client packages `meshcatalogclient` and `meshwantsclient`, built on `go/views`; 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; `go/views` already follows an owner's events durably and serves those two tools, so a second follower or a second pair of tools would be two forms of one thing; a view tool typed by the two seats would make mesh-sdk import contracts (ADR 0635 §4), so the typed half sits in the seats' client packages (ADR 0625 §4); a part waiting for the view is raised on the bound the seat list's readers keep | +| The seat list's reader and its two tools wired once by mesh-sdk's runtime | ADR 0636 §7 puts them in the runtime bundle; four hand-wired copies would each learn `acts` apart | +| The trunk's heads from the catalog half, and nothing read of the gate by the checker | ADR 0596 §2; with the hold and turns removed nothing of the gate is left for the checker to read | +| The indexer reads every contracts trunk's definitions at its start, before it judges | a definition merged while it was down is never judged against a list that lacks it (ADR 0636 §1) | +| Another node's running build read through the node-engine seat's verb `haves` | ADR 0660 §2 reads the manifest of the build each node runs, and that node is its one owner (ADR 0596 §2) | +| What the gate owes the forge said as the event `owed`, consumed durably by the forge bridge, and answered by `groups` | ADR 0596 §1, §2 end the bridge's watch of the gate's keys; a durable consumer loses no act and performs none twice; one verb answers a group and what it owes | +| A merge made at the forge opens a `forge-made` delivery record | its trace and outcomes are kept like every delivery's, and no later merge waits behind a merge the gate never heard | +| Every delivery part reaches the forge only through the seat `git`'s verbs | the delivery parts name no other module (ADR 0656 §1, acceptance test 27), and the forge's answers are typed in the seat's contract instead of guessed in each caller | +| A check that failed because a part of the mesh was not heard says `could not run`, naming the part, and is asked again on that part's `running` event | the operator sees a mesh fault apart from a failing change, and the check is asked again on the one event that can change its outcome, never on a timer (issue 1373) | | 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. @@ -814,12 +884,12 @@ say another runs a build (ADR 0363 §2); the gate's events are granted to the ga | 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` | +| an assignment graph revision kept | `assignment-revision` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.assignment-revision` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | | 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 wanted | `wanted` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.wanted` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | | an entry composed, and running healthy | `composed`, `running` | `node-engine`, the node-engine on each node | `mesh.seat.node-engine.event.composed.`, `.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 | @@ -847,8 +917,13 @@ on another node's subject is refused by the bus. 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 +list again, the view is read and steered by the two tools mesh-sdk's `go/views` already serves every part that takes a +first view: `_first_views` gains which view revision the part holds, what it waits for and since when, beside +what it heard and how far it is behind, and `_first_view_again`, granted as ADR 0636 §7's second is, makes it +read the view again now. `go/views` stays seat-neutral, naming no seat and no seat's type (ADR 0635 §4); the typed +first view of each half and the following of its events are the seats' client packages, `meshcatalogclient` for +`mesh-catalog` and `meshwantsclient` for `mesh-wants`, each built on `go/views` (ADR 0625 §4), so no part follows the +view by a follower of its own. 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. @@ -874,9 +949,11 @@ places; the builder and the grants part read their entries only through the seat 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. +the checker's watch of the indexer's `candidates.`, the forge bridge's read of the gate's `merges` and `owed`, +the checker's read of the gate's `source` and `turns`, the grants part's read of the assigner's state `wants`, the +checker's validator request with no seat definitions, a seat list compiled into the node-engine, the checker, the +builder or the grants part, a seat list's reader wired by a part itself, and a call of the forge module's own tools by +a delivery part; an old node-engine that wakes takes its new build in a form it reads. ## Order without a person @@ -899,7 +976,8 @@ per module and node from the keys: - **An SDK change ships with its users.** mesh-sdk is a library built into the modules that use it, so no module waits for it to be sent anywhere (ADR 0482 §1). - **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). + that holds one is told once on its pull request that the line is not read (ADR 0419 §2), until milestone G, in + which that notice goes with the gate's last read of a description (ADR 0659 §4; *The Approve and the merge*). ### An acting part's new version acts first, a wait with a bound @@ -1041,7 +1119,13 @@ commit on the SDK's trunk or the head of an SDK pull request the forge holds, op request's key and stops when the head in it moves, without asking the forge (issue 726); an ended check delivers no verdict, so none of its builds is reused. A trunk move never ends a check: its verdict goes out of date when it ends, and is carried. A check that failed because a part of the mesh was not heard is still asked - again when the nodes change, which judges heads no check judged. + again when the nodes change, which judges heads no check judged. From research 085's milestone its verdict says + `could not run`, naming the part not heard, and it is asked again on that part's `running` event of the + node-engine seat (*What each step says*), never on a timer; a second check of the same heads asked while one runs, + or one erred, claims nothing and writes no status, by the claim above (issue 1373; ADR 0548 §1, ADR 0656 §5). How + it is checked: in the checker's whole-module test a check whose composer's node does not answer says `could not + run` naming it, and is asked again once a fixture `running` event of that node is said; a second ask of the same + heads writes no status. - **A failed build node loses nothing.** An ask a build module took and never acknowledged goes to another node by the stream's own acknowledgement wait, with no timer of a part; a step's end is written on the build module's step key named by the ask's id (ADR 0525 §6). @@ -1103,7 +1187,22 @@ the check gates merges.* ### Nothing merges into a repository under check -*ADR 0548 §2 to §4, §9, §10, as ADR 0551 bounds them.* +*ADR 0548 §2 to §4, §9, §10, as ADR 0551 bounds them; from research 085's milestone removed, by the operator's word, +its record still to be written (below).* + +**Removed at research 085's milestone.** The hold, the merge turns, their stalled line, the gate's tools for them and +the setting `check-gates-merge` go in milestone G (the operator, on Telegram, 2026-11-09 01:11 UTC, choosing "Remove them +in G"). The setting has been false since 2026-10-25, so the gate wrote no turn and held no merge in that time, and the +operator's word for G is that no check gates delivery (1921's note of 2026-11-07). From G's delivery an approved merge +is handed to the forge bridge at once; a check running on a repository the merge goes into ends on the commit it read, +and its verdict is carried (*A move of the trunk carries every verdict*); the checker reads nothing of the gate, and +the gate no longer watches the checker's verdicts for a hold. **No record carries this yet:** ADR 0548 §2 to §4, §9 +and §10 and ADR 0551 are in force, and a record that ends them is written before this design is merged; until it is +accepted, the rule below holds as those records have it. How it is checked: in the gate's whole-module test an approved merge into a +repository under a running check is handed at once, and the gate's manifest declares no `turns` and no setting +`check-gates-merge`; in the checker's, a check claims at once with no gate state granted to read. + +Until milestone G's delivery: **Only while the check gates merges.** The hold, the merge turns and their stalled line hold only while the setting `check-gates-merge` of mesh-delivery-gate, set together with the forge module's, is true. While it is @@ -1374,12 +1473,12 @@ pull request, and it owes the forge nothing. ## The Approve and the merge -*ADR 0421, ADR 0398 §3, ADR 0406 §1.* +*ADR 0421, ADR 0398 §3, ADR 0406 §1, ADR 0659.* - **Asked as soon as the checks pass.** When a pull request's delivery reaches `ready` (both `mesh/merge-gate` and `mesh/repo-check` passed at its head against its trunk's head), mesh-delivery asks the operator's Approve of its merge into main at once, on a channel that proves who answered (ADR 0274; Telegram today). The ask names the - pull request, its issues, the reviewer's verdict, the modules it moves and the trunk commit the checks judged. A + pull request, its issues, the modules it moves and the trunk commit the checks judged, and no review (ADR 0659 §1). A verdict that names a trunk commit the trunk had already left raises no ask: the checker carries it to the trunk's head, and the carried verdict raises the ask (ADR 0421 §1, as ADR 0548 §8 changes it). - **A group is asked once.** One ask names every member and its head, once every member is `ready` and the @@ -1388,15 +1487,23 @@ pull request, and it owes the forge nothing. milestone is not asked (ADR 0493 §9, §11, which change ADR 0421 §1 and §2); the Approve it asks is also written into the wait list of the session a `Waited-by: @` line on the pull request names, and only when the pull request carries that line; without it the pull request is asked as today (ADR 0491 §3, §5). -- **The reviewer's verdict is attached, not a gate.** The review finishes before the push (playbook 07), and the - session that opens a pull request gives its review record: who reviewed it, the commit the review approved and - the verdict; from the delivery of issue 1782 a verb of the delivery seat records it at the head it judged, and no - description is read (ADR 0493 §5, §6). The ask carries it; with none, or one naming another commit, the ask says so in its first line and - the operator decides (ADR 0421 §5). +- **The review is the session's, and no part reads it.** The review finishes before the push (playbooks 07 and 08); + the session may name its reviewer in the description for people, and nothing reads it: the gate raises the ask + without any review line, the ask carries no review record, and an edit of the description withdraws nothing + (ADR 0659 §1 to §3, which supersedes ADR 0496 and changes ADR 0493 §5, §9, §12). A review verb of the delivery + seat, if issue 1782 builds it, records a review people may read and gates nothing (ADR 0659 §2). Every wait of the + gate is on a state or an event of the bus (ADR 0659 §5). +- **The description's last three reads go in milestone G.** The issue numbers the ask names, the author a stalled + line names and the one notice that `after:` lines are not read are the three reads of a description ADR 0659 §4 + leaves until they move to the bus. In milestone G the issue numbers come from the issue tracker's links of the + milestone (ADR 0493 §4, issue 1783), the author from the forge's pull request record, read through the seat + `git`'s verb `pull`, and the notice goes, so no part of the mesh reads a description at all. How it is checked: a + test over the gate's code, beside ADR 0659 §6's rows, finds no read of a description field. - **The merge.** mesh-delivery merges through the forge's merge tool at the heads the Approve names, only while they are still the heads, their check passed against the trunk's head, and, while the check gates merges, no check of other heads runs on a repository the merge goes into (*Nothing merges into a repository under check*, - above). When the trunk moved, the merge waits for the carried verdict, and the event of its line asks the merge + above, which milestone G removes). From milestone G the forge bridge merges through the seat `git`'s verb + `merge-pull`, which keeps the merge tool's judgement of ADR 0534 §4. When the trunk moved, the merge waits for the carried verdict, and the event of its line asks the merge again; no timer asks it, no person rechecks, and the Approve is not asked again (ADR 0421 §3, §12, as ADR 0548 §8 changes them). The merge tool still refuses a verdict naming a trunk commit other than the trunk's head and asks nothing (ADR 0534 §4); the carry makes the verdict current. @@ -1405,7 +1512,7 @@ pull request, and it owes the forge nothing. is said once (ADR 0421 §6). - **A failure is said, never merged past.** An approved head whose check failed is said once on the pull request and listed by `stalled`; so is a merge the merge tool kept refusing for an hour (ADR 0421 §7). -- **Recorded.** The ask, the answer with its warrant and the review record are kept on the delivery, shown by +- **Recorded.** The ask and the answer with its warrant are kept on the delivery, shown by `show` and said in mesh-delivery's log; the merge's transition names the Approve (ADR 0421 §8). ## After the merge: the source revision and the wants @@ -1498,6 +1605,15 @@ timer. A group's merges arrive together. held group (ADR 0441 §3, §4); the gate passes the group's end by without counting it a refusal. A later merge in a member's repository waits behind the group; a merge in another repository is taken in alone, unless a revision is pending for its indexer (ADR 0633 §4, which builds ADR 0429 §11 for take-in). +- **A merge the gate did not make is taken in like any other.** When the forge says a pull request merged and the + gate holds no merge of it (a person merged it in the forge's own page), the forge bridge reads that pull request + by its merge commit (from milestone G through the seat `git`'s verb `pull`) and publishes its merged message with + the merge's trees as for any merge; the indexer takes it in under the bullets above, and a later merge's take-in + that its repository's taken merge covers ends with it, so nothing waits behind it. The gate opens a `forge-made` + record for it (*The delivery record*). The design's choice under ADR 0633 §1 and §4, as the operator asked it (1921's + note of 2026-11-07); built in the issue 1966 recovery (mesh-delivery #185, #187), kept whole in milestone G. How it is + checked: in the forge bridge's whole-module test a merge with no merge of the gate's publishes its merged message, + and in the indexer's take-in table a merge its taken merge covers is ended. - **A wait is bounded, said and recoverable.** The indexer's tool `indexer_pending` reads the pending revisions and the members still gathered, each with its merges, the indexer build it waits for, since when and `refused`; the view names a module waiting for its revision's indexer through that tool (ADR 0633 §5, which changes ADR 0603 @@ -1682,7 +1798,7 @@ later change of a kept data's form, not as a case of this milestone. - **How placement is changed.** The seat `mesh-module-assigner`, held once for the mesh by the assigner, keeps three states: `placement`, one key `placement.` per node; `proposals`; and `candidates`. Only the holder's credential writes them, and the contract (the states' layouts among it) is the seat's, in - `mesh-placement-contracts` (ADR 0634 §6, §7). No repository holds + `mesh-contracts`, module assignment being of the Core domain (ADR 0634 §6, ADR 0641 §1). No repository holds placement and none names a node: what differs between nodes is bus state, and a build module's capacity and build hours are values on each node's layer of mesh-config. A key holds what a person decides of the node and nothing a machine observes: @@ -1726,7 +1842,7 @@ later change of a kept data's form, not as a case of this milestone. holder hears that key's change; nothing polls. The nodes that must answer are each node whose key the proposal changes and each node whose composition the holder finds changed with the current inputs on both sides, not every placed node, so a node gone for good or asleep blocks only a proposal that touches it. The contract (the state, - its key, the answer) is the seat's, in `mesh-placement-contracts` (ADR 0567, ADR 0634 §6, §7); the composition with + its key, the answer) is the seat's, in `mesh-contracts` (ADR 0567, ADR 0634 §6, ADR 0641 §1); the composition with a given placement is the composition of `mesh-delivery-sdk` (ADR 0635 §4), and the comparison is the node-engine's own package (ADR 0634 §1). - **The holder judges the answers and the operator approves.** The holder computes, by node, the modules the @@ -1767,6 +1883,13 @@ later change of a kept data's form, not as a case of this milestone. the node composing it keeps what runs until the vault holds it. Likewise a consumer of `model-access` answered by a licence record, placed before the vault serves licences (ADR 0550 §3, issue 569), is refused by the same guard and its node keeps what runs; none is placed today. +- **A claim problem holds its module, never the node's pass.** When a module's claim cannot be held (its seat's + definition missing, removed or refused, a claim another holder keeps, a credential the vault refuses), the node agent + composes that module as left out, keeping its openings, says why in its have, and composes and applies every other + module of the node; it never refuses the whole pass for one module's claim (ADR 0636 §3: a refused definition blocks + new claims of that seat only). Met on 2026-11-08, when one claim problem refused a node's every module (issue 1966). + How it is checked: in the node-engine's composition table a node with one module whose claim is refused applies the + others, and that module's have names why. - **The order a node takes to composing its own.** One delivery group carries every part: the seat `mesh-module-assigner` on the assigner, the controller's verb `placement-export` (as it is) and the controller's pin only, the node-engine (its record and its own composer), the vault's `own`, the grants part's reads, and mesh-config's reading of @@ -1975,7 +2098,7 @@ forge module's files of what it announced (ADR 0406 §6). | Verb | Answers or does | |---|---| | `deliveries` | every delivery not final, the final ones of the last day, and every one `stalled` still lists | -| `show` | one delivery or group whole: the Approve, transitions, the review record, the source revision its merge moved | +| `show` | one delivery or group whole: the Approve, transitions, the source revision its merge moved; no review record from milestone G (ADR 0659 §1) | | `groups` | every group with its members and its derived state | | `table` | the state table, with its bounds | | `stalled` | every merge or check held past its bound | @@ -1989,8 +2112,9 @@ 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` | +| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656), and the acts the gate said it owes the forge for it, each with its id (*The delivery record*) | +| `owed` | an act the gate owes the forge (a merge, a status, the delivery's view, a comment, a line on the commit's note), with its pull request, head and id, said only from the gate's account and heard by the forge bridge through a durable consumer (*The delivery record*) | +| `milestone.asked`, `milestone.withdrawn`, `milestone.answered` | the one Approve ask per milestone (ADR 0493 §9 as ADR 0659 §1 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*) | @@ -1998,8 +2122,42 @@ From research 085's milestone the seat gains: 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 +**The seat `node-engine`** gains, from research 085's milestone, the verb `haves`: what its node runs of each module, +the fingerprint and its phase, which the node-engine of another node and the grants part ask to decide a seat's pass +(ADR 0660 §2; *Who holds a seat is applied by one rule*), and the gate's `gate_record_repair` asks for a missing +outcome. It answers from the node's own haves, and every node's account may call it. + +**The seat `git`.** The seat `git` (design 26, *The git seat*), of the Delivery domain, defined in +`mesh-delivery-contracts`, holds no verb today, and the delivery parts reach the forge by naming the forge module's own +tools and guessing the shape of their answers (on 2026-11-09 one guess, an answer wrapped in a `result` field, made +316 conditions stand, issue 1966). From research 085's milestone the seat's definition carries the verbs below, their +inputs and answers typed in its contract, served by the module that claims the seat; every delivery part calls the +seat, never the forge module by name (acceptance test 27). The verbs are this design's choice under ADR 0596 §2 (a +module's data is asked through its seat verbs) and ADR 0625 §1 (a seat's contract is generated from its definition), +each for a call a delivery part makes of the forge today: + +| Verb | Answers or does | Called by | +|---|---|---| +| `pull` | one pull request, by its repository and number or by its merge commit: number, base, head branch, head, merge commit, author, state | the forge bridge (a merge the gate did not make), the gate's author of a stalled line (*The Approve and the merge*) | +| `open-pulls` | every open pull request of every repository, its head and base, the statuses its base requires and those its head has | the forge bridge, at its start and each reconnect (ADR 0443 §5) | +| `tags` | a repository's tags, paged, with the page's bound said when more exist | the forge bridge, for the builder's floor | +| `trees` | the trees of one commit | the forge bridge (a merged message's trees), the checker, the indexer (the definitions at its start) | +| `file` | one file at one commit | the checker, the indexer | +| `commits` | the commits between two commits of a repository | the checker | +| `branches` | a repository's branches with their heads | the checker | +| `contains` | whether one commit contains another | the indexer | +| `status`, `comment`, `view`, `note` | a commit status, a comment, the delivery's one view comment, a line on the commit's note under `refs/notes/mesh-plan` | the forge bridge alone, performing the gate's `owed` | +| `merge-pull` | merges a pull request at a head, judging what the forge module's merge tool judges today (ADR 0534 §4) | the forge bridge alone | + +The verbs that act are granted to the forge bridge's account alone, so the bridge stays the one part that acts on +the forge; the reads are granted to each part that declares them in its `invokes`. A verb's answer is the contract's +type, never a wrapper a caller unwraps. How it is checked: the seat definition's conformance fixtures hold one answer +of each verb, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's and the +indexer's unit fakes answer from the same fixtures; a call of a verb that acts from an account other than the forge +bridge's is refused by the bus; live, a search of the delivery parts' code finds no name of the forge module's tools. + +**Links of the trace.** No part reads a review line (ADR 0659 §1 to §3); who may review and the review verb's grant +stay issue 1782's, outside milestone G, and that verb gates nothing (ADR 0659 §2). 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 @@ -2011,6 +2169,14 @@ an issue linked to a decision and to a research effort reads both back; a decisi 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. +**A session hears that a record was accepted** (issue 595). The knowledge base already says, on its holder's own +stream, the event `reviewed` for every judgement of a record; from milestone G that event carries, when the operator's +Approve accepts a record, that it was accepted and by the operator, with the warrant, and the tool runner's account is +granted to hear it, so a session waiting on a record wakes on it. The design's choice under ADR 0596 §1 (a module's +events are subscribed to): a new event of the seat `knowledge-base` would be a second path for the same fact (lead-47, +2026-11-09). How it is checked: in the knowledge base's whole-module test an Approve that accepts a record says +`reviewed` with what was accepted and by whom, and the tool runner's account hears it. + **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). @@ -2179,7 +2345,8 @@ development, the check's share about a minute): From ADR 0548 and ADR 0551 (issue 568): 25. **One check per delivery.** A delivery group of two repositories is under check, and a lone pull request into - one of them is approved during the run. While the check gates merges, it waits, `mesh-delivery.show` says + one of them is approved during the run. From milestone G it merges at once, as it does today with the setting + false, and nothing of the hold is left to read (test 52). Until then: while the check gates merges, it waits, `mesh-delivery.show` says what it waits for, and it merges when the group's verdict is written; while it does not, it merges at once and the group's running check ends on the commit it read. Either way the group's verdict is then carried to the new trunk commit with no test run for an unchanged digest, and the group merges on its Approve with no second @@ -2268,7 +2435,7 @@ Each change ADR 0656 makes in an earlier record, and its test: 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, + keep running what they run, answer in `_first_views` the revision they wait for 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 @@ -2277,6 +2444,28 @@ Each change ADR 0656 makes in an earlier record, and its test: 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 milestone G's prepare found, and its test: + +50. **No description is read** (ADR 0659 §4). The Approve ask names the issues read from the milestone's links and a + stalled line the author read through the seat `git`, no notice of an `after:` line is said, and a test over the + gate's code finds no read of a description field (whole, gate). +51. **The forge is reached through its seat.** Every act the forge bridge performs and every read the forge bridge, + the checker and the indexer make of the forge goes through a verb of the seat `git`, each answer decoded as its + contract's type; an act called from an account other than the forge bridge's is refused; a search of the + delivery parts' code finds no name of the forge module's tools (whole, forge bridge; live). +52. **Nothing holds an approved merge for another check.** An approved merge into a repository under a running check + is handed at once; the gate declares no `turns` and no setting `check-gates-merge`, and the checker is granted no + read of the gate's state (whole, gate and checker; its record first, *Nothing merges into a repository under + check*). +53. **What the gate owes is said, and a forge-made merge is kept.** An `owed` said while the forge bridge is stopped + is performed once after its restart; a merge made at the forge opens a `forge-made` record and is taken in, and a + later merge in its repository follows it (whole, gate and forge bridge). +54. **One reader of the seat list per part, and one view machinery.** Every part that reads the seat list has the two + tools of ADR 0636 §7 from the runtime and no wiring of its own; every part that takes a first view answers its + held and awaited revision in `_first_views` (unit, mesh-sdk; live search). +55. **A claim problem holds one module.** A node with one module whose claim is refused applies the others, and that + module's have names why (unit, node-engine). + ## What retires - The "release branches" and the controller's check of them (ADR 0358, superseded by ADR 0401). @@ -2326,8 +2515,11 @@ Each change ADR 0656 makes in an earlier record, and its test: 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. + list compiled into a part, and every read of another module's bus state the revision view replaces (ADR 0656); the + gate's `merges`, carried into the delivery records and written no more, and its `owed`, said as events; the hold, + the merge turns, their stalled line and tools and the setting `check-gates-merge`, once their record is accepted; + the last three reads of a pull request's description (ADR 0659 §4); every delivery part's call of the forge module's + own tools, for the seat `git`'s verbs; and the four seat-list readers the delivery parts wire by hand. ## Milestones @@ -2378,7 +2570,7 @@ release reports the controller's code lines against research 063's baseline (acc | L6 | Provisioning leaves the controller (ADR 0570): every provision is one key of the seat `mesh-provisioner`, held by mesh-provisioner, holding a reference and never a value, and each placed module's provisioning plan a key of its state `plans`, both composed from placement, the wants and the manifests on change events; each provider acts once all a provision depends on is provided; the assigner moves a want only once its build's provisions are provided; a provision no want needs is retiring, and its account or credential is removed only on the operator's Approve, its data never; the vault mints each provision's secret sealed to the consumer's and the provider's node, and the consumer's node-engine writes it; a wait for a person is a provision's state, which the watchman reads; bus permissions stay with the grants part (ADR 0349, ADR 0230, ADR 0547 §11) | — | mesh-sdk, the node-engine (`mesh-host`), mesh-tools (the tool runner's provisioner client), mesh-catalog (mesh-provisioner, the vault, the provisioner client, postgres, keycloak, gitea, route-adapter), mesh-delivery (assigner, watchman) | 1954 | | L7 | History and records leave the controller (ADR 0571): the builder keeps its build log, one entry at a build's start and one at its end, from which build history and durations are read and the module mesh-catalog asks the builds it missed; the artifact sweep is the seat `mesh-artifact-sweep`, held by artifact-store-tools beside the artifact store, its keep set from the store's own listing, the wants and the build log, sweeping at each build's end from the lease write and recording each deletion in the hand-act log; disk readings are disk-load's own state on every node; who holds which seat is the state `seat-holders` of the seat `mesh-placement` (now `mesh-module-assigner`), and tools are found by the tool runners' announcements; what a node runs is its want and its have; the grants part writes `grants..` 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: every check, a lone pull request's as a group of one and a verdict carried after a trunk move included, is judged by a checking program built from the change's own heads in the build seat's sandbox, which also makes the heads' one seat list that the check hands the manifest validator and the composer; it brings no seat definition and no manifest field, so the running checker checks it (ADR 0644; *The bootstrap delivery, and the one milestone after it*) | 26 needs it | mesh-sdk (`go/delivery`: the seat `node-build-go`'s ask `run` and the checker's record of which checking program judged; `go/composer`: the request's seat list), mesh-catalog (mesh-build-go: the run step, the refused answer, the kept programs' tools and sweep), mesh-delivery (checker: the checking program and its command), hq (this design, the glossary) | 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 | +| 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); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check | 26 to 55 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker, builder, grants part, gate's view, forge bridge, watchman), mesh-catalog (the gate, the build modules, the forge module, 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, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | 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 @@ -2456,10 +2648,10 @@ order inside one milestone, for this one case (ADR 0644). the first seat G defines; a program built from the heads reads what the heads define. - **Where its types live.** B0 adds the ask `run`, its answer and the verdict's record of which checking program judged it where the seats' types live on today's trunk, mesh-sdk's `go/delivery`, and the request's `seats` where the - composer's request lives, mesh-sdk's `go/composer`. `mesh-delivery-contracts` and `mesh-delivery-sdk` are created by - the registry group, which joins milestone G (ADR 0634 §7, §8), so they exist on no trunk before G; G's merge carries - these types with `go/delivery` into the seats' contracts in `mesh-delivery-contracts` (ADR 0634 §4) and the request - with `composer` into `mesh-delivery-sdk` (ADR 0635 §4), and mesh-sdk keeps no copy. The checking program, its rules + composer's request lives, mesh-sdk's `go/composer`. The registry milestone, merged after B0 and before milestone G, + made `mesh-delivery-contracts` and `mesh-delivery-sdk` (ADR 0634 §7, §8) and carried these types with `go/delivery` + into the seats' contracts in `mesh-delivery-contracts` (ADR 0634 §4) and the request with `composer` into + `mesh-delivery-sdk` (ADR 0635 §4); mesh-sdk keeps no copy, and milestone G builds on them where they are. The checking program, its rules and its command `mesh-checking-program` are the checker module's own package in mesh-delivery, and the run step is mesh-build-go's own (ADR 0634 §1). - **The trunk's validator still judges inside a check.** The indexer's new manifest is also validated with the @@ -2494,8 +2686,7 @@ B0's own choices, each checked by the rows of *How it is checked* below: checking program (ADR 0644, Consequences), and every check costs one more ask in the build seat, within the checker's step bound (ADR 0639 §5), the program built once per build and reused. - **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 + view, the two graphs, the difference, the acting rules, the delivery record and the trace's links. 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 @@ -2521,8 +2712,18 @@ B0's own choices, each checked by the rows of *How it is checked* below: ## 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). +- Who may review a change, and the review verb's grant: issue 1782, outside milestone G; no part reads a review line + meanwhile, and the verb, if built, gates nothing (ADR 0659 §2, §3). +- **A session's merge of a recovery fix.** The operator asked that a session merge a recovery fix through a verb of + the seat `mesh-delivery`, recorded in the gate like any merge and said as an event, with no Approve (1921's note of + 2026-11-07; the instruction the operator approved on 2026-11-09 says the session uses that verb once the seat serves + it). No record decides a merge without the operator's Approve: ADR 0421 §1 and ADR 0398 §3 ask it for every merge, + and the forge's merge tool merges only a head the operator approved (ADR 0534 §4). The shape this design would give + it, once a record decides it: the verb `merge` of the seat `mesh-delivery`, taking the pull requests at their heads + and the reason, refused unless their check passed, opening a delivery record marked `recovery` with the caller as + the runtime proves it, and saying the event `recovery-merged`, which the teller tells the operator on every channel, + so a recovery merge cannot go untold. Until that record is accepted it is not built, and a recovery fix is asked the + operator's Approve like any change. - 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 @@ -2566,6 +2767,17 @@ on the step key, the wording of a failed program's verdict as an exception until verdict's program is built; each is now written as built, with its reason. The lead's rulings on that round then moved the input onto the checker's verb `input` (ADR 0596 §2) and the cycle rule into the checking program (ADR 0656 §5). +The amendment for milestone G's prepare (2026-11-09) writes what that prepare found where the records and the code +or this design disagreed: the homes of `mesh-module-assigner` and `mesh-wants` after ADR 0641; ADR 0659's end of every +description read and ADR 0660's one rule for a seat's pass; the checker's and the forge bridge's reads of the gate's +state replaced (the trunk's heads from the catalog half, `owed` said as an event and answered by `groups`); the hold +and the merge turns removed by the operator's word, their record still to be written; a merge made at the forge taken +in and recorded; the indexer reading every definition at its start; the seat `mesh-grants` defined in its domain's +contracts module; the seat `git`'s verbs for every call the delivery parts make of the forge; the seat list's reader +wired once by the runtime; `go/views` as the view's machinery, with the seats' client packages typed over it; a check +that could not run asked again on the missing part's `running`; and a claim problem holding one module. Its review by +architectural domain is still to come, and this section is rewritten with its findings. + 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 -- 2.54.0 From 2fd96e1e4e0541773d301169e63f9170ddc2b130 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 13:10:40 +0100 Subject: [PATCH 2/8] Take in round 1 of design 47's G review: hear what nodes run, say what the forge is owed, reach the forge by its seat The reviewers found a seat's pass waiting on a call to a node that may be away, acts owed to the forge that could be lost or done twice, a seat git that missed the gate's and half the parts' forge calls and events, a merge that still judged a check the operator removed, and waits with no bound or condition. The design now keeps what each node runs from its running events, makes every owed act safe to repeat with its outcome said and recorded, gives the seat git every call and event with its act verbs granted by the holder's callers, orders forge, bridge and gate with a way back, adds module-held and check-could-not-run, numbers tests 56 to 60, and says how the store seat's holder takes its own build. Claude-Session: https://claude.ai/code/session_01NcbgfFfQ4Z9UE5jK32PmE5 --- 00-META/glossary.md | 7 +- 03-DESIGN/01-to-be/26-the-seats.md | 8 +- .../47-delivery-from-commit-to-delivered.md | 356 +++++++++++++----- 3 files changed, 264 insertions(+), 107 deletions(-) diff --git a/00-META/glossary.md b/00-META/glossary.md index 3510aea2..e2a9b1c5 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -813,9 +813,10 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel - **git seat** — the seat `git`, held once for the mesh, delivering the provision `git`: the repositories a build comes from ([26 — The seats](../03-DESIGN/01-to-be/26-the-seats.md) *The git seat*). A seat of this domain, defined in `mesh-delivery-contracts`. From research 085's milestone its verbs are the one way a - delivery part reaches the forge: reads (`pull`, `open-pulls`, `tags`, `trees`, `file`, `commits`, `branches`, - `contains`) for the parts that declare them, and acts (`status`, `comment`, `view`, `note`, `merge-pull`) for the - forge bridge alone (design 47, *The verbs*). Not the **forge** itself, the server, nor the forge module's own tools. + delivery part and the gate reach the forge: reads (`pull`, `open-pulls`, `tags`, `trees`, `file`, `contains`, + `statuses`) for the parts that declare them, acts (`status`, `comment`, `view`, `note`, `merge-pull`) for the forge + bridge alone, as the holder's trusted value `callers` names, and the forge's events (`pull.*`, `forge.*`, + `repo.writable`) as the seat's (design 47, *The verbs*). Not the **forge** itself, the server, nor the forge module's own tools. ## Health and repair — what is wrong, and putting it right diff --git a/03-DESIGN/01-to-be/26-the-seats.md b/03-DESIGN/01-to-be/26-the-seats.md index fc9547b1..a7a37bdb 100644 --- a/03-DESIGN/01-to-be/26-the-seats.md +++ b/03-DESIGN/01-to-be/26-the-seats.md @@ -256,7 +256,7 @@ defined whole or at the seat list's grain (ADR 0635 §2). In the milestone of AD | contracts module | domain | seats | held by | |---|---|---|---| - | `mesh-delivery-contracts` | Delivery | `mesh-catalog`, `mesh-delivery-checker`, `mesh-delivery`, `mesh-wants`, `node-build-go`, `node-build-image`, `node-build-node`, `npm-package-registry`, `go-package-registry`, `container-image-registry`, `oci-artifact-registry` | the indexer, the checker, the gate part, the assigner, the three build modules, the forge (two), `distribution` (two) | + | `mesh-delivery-contracts` | Delivery | `mesh-catalog`, `mesh-delivery-checker`, `mesh-delivery`, `mesh-wants`, `git`, `node-build-go`, `node-build-image`, `node-build-node`, `npm-package-registry`, `go-package-registry`, `container-image-registry`, `oci-artifact-registry` | the indexer, the checker, the gate part, the assigner, the three build modules, the forge (three), `distribution` (two) | | `mesh-health-and-repair-contracts` | Health and repair | `mesh-conditions`, `mesh-self-check`, `mesh-healers`, `mesh-watchdogs`, `mesh-hand-acts` | the condition keeper, mesh-self-check, mesh-healers, the watchman, mesh-hand-acts | | `mesh-provisioning-contracts` | Provisioning | `mesh-provisioner` | mesh-provisioner | | `mesh-identity-and-access-contracts` | Identity and access | `mesh-vault`, `mesh-node-enroller` | the vault, the module `mesh-enrolment` | @@ -691,8 +691,10 @@ With the seat unheld, a build from the seat is refused and says why. External bu **The seat's verbs, from research 085's milestone.** The delivery parts reach the forge through the seat `git`'s verbs alone, never by the forge module's own tools: reads for the parts that declare them in their `invokes`, and -acts (a status, a comment, the delivery's view, a line on the commit's note, a merge) for the forge bridge alone. -Design 47, *The verbs*, lists them with their callers and how they are checked. +acts (a status, a comment, the delivery's view, a line on the commit's note, a merge) for the forge bridge alone, as +the holder's trusted value `callers` names; the forge's events (a pull request updated, merged or closed, the forge's +own webhooks, a repository writable again) are the seat's events. Design 47, *The verbs*, lists them with their +callers and how they are checked. **Not yet designed:** a credential for cloning a private repository. The mesh's own repositories are public. The natural place for a clone credential is a `secret` from the vault, and that is a decision 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 bdbb118a..07f1605e 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 @@ -184,8 +184,8 @@ source revision is derived from the keys: every **want** equals its module's **f - **The forge bridge** turns the forge's events into stream messages and is the only part that acts on the forge. From research 085's milestone every delivery part reaches the forge through the seat `git`'s verbs alone, - never by the forge module's own tools: the forge bridge for every act and read it makes, the checker and the - indexer for the reads they make (*The seat `git`*, under *The verbs*). + never by the forge module's own tools or events: the forge bridge for every act and read it makes, the checker, + the indexer and the gate for the reads they make and the events they hear (*The seat `git`*, under *The verbs*). - **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). From research 085's milestone it keeps the catalog graph per @@ -487,34 +487,14 @@ ADR 0656. - **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`. -- **What the gate owes the forge is said, not watched.** The forge bridge today performs what the gate's states - `merges` and `owed` ask (a merge, a status, the delivery's view, a comment, a line on the commit's note) by watching - those keys (ADR 0437 §8), a read of another module's bus state ADR 0596 §2 ends when the gate moves. From research - 085's milestone the gate says each act it owes as the event `owed` of the seat `mesh-delivery`, carrying the act, - its pull request and head, and an id; only the gate's account publishes it (ADR 0363 §2). The forge bridge hears it - through a durable consumer declared in its `consumes`, performs each id once, keeping which ids it performed in its - own state, and does it through the seat `git`'s verbs. The verb `groups` also answers, per group, the acts the gate - said with their ids, and the bridge's tool `bridge_owed` the ids it performed and when, so a person reads both sides - through their owners and nothing reads the other's state. This - is the design's choice under ADR 0596 §1, §2 and ADR 0656 §7: an event consumed durably loses no act while the - bridge is down and performs none twice, and one verb answers a group and what it owes (lead-47, 2026-11-09: no - second verb). How it is checked: in the forge bridge's whole-module test an `owed` said while it is stopped is - performed once after its restart, an act said twice is performed once, its account cannot read `owed`, and - `bridge_owed` lists what it performed; in the gate's, `groups` answers each act it said for a group, with its id. -- **A merge made at the forge has a record too.** A merge the forge reports that the gate did not make (a person's - merge in the forge's own page, as on 2026-11-07 and 11-09) is taken in like any other merge (*Taken in by the - indexer it names*), and the gate opens a delivery record for it on the forge bridge's merged message, marked - `forge-made`, with no Approve, so its trace and its outcomes on the nodes are kept like every delivery's and no - later merge waits behind a merge the gate never heard. The design's choice under ADR 0656 §7 and ADR 0633 §1 (the - operator, 1921's note of 2026-11-07: "a merge made in the forge is taken in like any other"). How it is checked: in - the gate's whole-module test a merged message with no record of the gate's opens a `forge-made` record, and a later - merge in the same repository is taken in behind it. +- **What the gate owes the forge is said, not watched, and a merge made at the forge has a record too** (*What the + gate owes the forge*, below). - **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 verb `haves` of that node; applies the answers as the consumer would) | + | 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 verb `composition` 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 and the acts said for it | @@ -533,6 +513,71 @@ ADR 0656. 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). +### What the gate owes the forge + +*ADR 0596 §1, §2, ADR 0656 §7, ADR 0437 §8, ADR 0441 §5, ADR 0633 §1, §4, ADR 0363 §2, ADR 0564 §2; from research 085's +milestone, this design's choices under ADR 0596 and ADR 0656 §7.* + +The forge bridge today performs what the gate's states `merges` and `owed` ask (a merge, a status, the delivery's +view, a comment, a line on the commit's note) by watching those keys (ADR 0437 §8), and the forge module writes the +checker's `mesh/check` from a watch of the checker's state `owed`: reads of another module's bus state that ADR 0596 +§2 ends when their owner moves. From research 085's milestone: + +- **Every act is an event, under one id.** The gate says each act it owes as the event `owed` of the seat + `mesh-delivery`, on `mesh.seat.mesh-delivery.event.owed..`, carrying the act, the pull request, + the head and an id; the checker says its status the same way, as the event `owed` of the seat + `mesh-delivery-checker` on `mesh.seat.mesh-delivery-checker.event.owed..`. Only the owner's + account publishes each (ADR 0363 §2). The subject per repository and pull request lets the bridge, a person and a + test read one pull request's acts alone. The seat events are kept on each owner's stream `owed`, 7 days and at most + 64 MiB (an estimated 20 deliveries a day, with about 15 acts of about 1 KiB each, is about 2 MiB over 7 days). +- **Every act is safe to repeat at the forge.** A status and the delivery's view replace what stands; a merge of a + pull request already merged at that head is answered performed; a comment and a line of the note carry the act's + id, and the seat `git`'s verb skips an id the pull request or note already holds. This is chosen over recording the + id before the act: an id recorded and then not performed (the bridge stopped between the two) would be lost, while + an act repeated changes nothing. The bridge's own state `performed`, a key-value state in the new form (ADR 0595), + form 1, one key per id with the outcome and when, kept 7 days as the stream is and capped at 16 MiB, is therefore a + saving and a read, never what keeps an act from happening twice. +- **Every outcome is said, and the gate records it.** The bridge says each act's outcome on its own stream `forge` as + the event `owed.ended`, with the id and one of `performed`, `refused` (the forge's own words), `could not be + performed` (the forge did not answer within the call's bound, with the error) or `undecodable` (an act it cannot + read, with why): an act it cannot decode is said, never only logged. The gate hears the stream `forge` through its + durable consumer, writes the outcome into the delivery record, and says an act's id is answered only once an + outcome is recorded. +- **What is unanswered is said again, and stalls by a bound.** At its start the gate says again, under the same id, + every act its records hold unanswered. An act unanswered 15 minutes after it was said is listed by `stalled`, which + the condition keeper raises as the stalled line (ADR 0421 §7, ADR 0564 §2): fifteen minutes is many times a + forge call's time, and short beside the check's hour. +- **A group's merges follow one another.** The gate says a group's next `merge-pull` only once the previous member's + merged message is heard (ADR 0441 §5), so a member the forge refuses holds the rest, and none is merged ahead. +- **The bridge catches up from the seat.** At its start the bridge resumes its durable consumer and also reads + `groups`, which answers, per group, the acts the gate said with their ids and their recorded outcomes; an id said + and not answered is performed. Its tools: `bridge_owed` (the ids it performed with their outcomes, and its + consumer's backlog: the last event applied, the events waiting, the age of the oldest), `bridge_owed_again` + (perform one id again now) and `bridge_owed_drop` (drop one id with its reason, written in the hand-act log of the + seat `mesh-hand-acts`, and said as `owed.ended` with the outcome `dropped`). +- **The switch carries what is owed.** At the new gate's start (*The switch carries today's state in, once*) every key + of `owed` and `merges` the old bridge has not performed becomes an act of its record, with an id made of the key and + its revision; the new bridge counts as performed every id whose key and revision the old bridge's own bookkeeping + names, so nothing is done twice and nothing dropped. A merge handed to the forge during the switch is such an act: + its merged message finds the record holding it, so it opens no `forge-made` record. +- **A merge made at the forge has a record too.** A merge the forge reports for which no record holds a `merge-pull` + of that pull request at that head (a person merged it in the forge's own page, as on 2026-11-07 and 11-09) is taken + in like any other merge (*Taken in by the indexer it names*), and the gate opens a delivery record for it, marked + `forge-made`, with no Approve, so its trace and its outcomes on the nodes are kept like every delivery's and no + later merge waits behind a merge the gate never heard. The gate says it as the delivery's `transition` with the why + `forge-made`, which the teller passes on to the operator (the operator, 1921's note of 2026-11-07: "a merge made in + the forge is taken in like any other"). +- **How it is checked.** In the forge bridge's whole-module test: an `owed` said while the bridge is stopped is + performed once after its restart; the bridge stopped between an act and its `owed.ended` performs it again on its + restart and the forge holds it once (the comment and the note by their id); an act said twice is performed once; an + undecodable act is said `undecodable`; `bridge_owed`, `bridge_owed_again` and `bridge_owed_drop` answer, the drop + writing the hand-act log; its account cannot read the gate's or the checker's state. In the gate's: an act left + unanswered is said again at the gate's start under its id and listed by `stalled` after 15 minutes; a group's second + `merge-pull` is said only after the first member's merged message; the switch with a merge handed and not yet + merged makes no `forge-made` record; a merged message with no record's merge opens a `forge-made` record, said as + `transition` with why `forge-made`, and a later merge in the same repository is taken in behind it. Acceptance + tests 53 and 51. + ## The state table *ADR 0539 §17.* @@ -732,11 +777,16 @@ Findings 11). How each is kept, read and retained is this design's choice under manifest claiming that seat is judged with it. - **Who holds a seat is applied by one rule, from what each node runs.** The node-engine and the grants part decide a seat's pass by mesh-sdk's `SeatsToPassOn` (ADR 0660 §3), reading the manifest of the build each assignment runs - (ADR 0660 §2). The build another node runs is read through the node-engine seat's verb `haves` of that node, never - its have key, its want or the assignment half (ADR 0596 §2; *Each part acts on its own entries*). The verb is the - design's choice under ADR 0660 §2: the node that runs a build is the one owner of what it runs. How it is checked: - in the node-engine's and the grants part's whole-module tests a pass is decided from a stand-in seat answering - `haves`, and neither part's account can read another node's have key. + (ADR 0660 §2). What each node runs is heard, never asked: each part keeps it from the node-engine seat's events + `running.` (*What each step says*), through a durable consumer of its own declared in its `consumes`, so a + pass is decided with no call to a node that may be away. The event carries the module, the fingerprint and the + digest of the build's manifest, which is the indexer's manifest of that build (ADR 0660 §2's "the indexer's + manifest of it"), read by digest from the catalog half; the builder's done key is not needed, since the event names + the digest it would give. A node that has said no `running` for a module since the part's consumer began counts by + the trunk's manifest, as ADR 0660 §2 has it where the running build cannot be read, and both parts apply that same + fallback in the one function (ADR 0660 §3). Never another node's have key, its want or the assignment half (ADR 0596 + §2; *Each part acts on its own entries*). The design's choice under ADR 0660 §2: the node that runs a build says what + it runs, and an event kept by a durable consumer answers when that node is away. How it is checked: test 56. - **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 @@ -860,11 +910,15 @@ ADR 0656 decides the direction and leaves every choice made in working it out to | The seat list's reader and its two tools wired once by mesh-sdk's runtime | ADR 0636 §7 puts them in the runtime bundle; four hand-wired copies would each learn `acts` apart | | The trunk's heads from the catalog half, and nothing read of the gate by the checker | ADR 0596 §2; with the hold and turns removed nothing of the gate is left for the checker to read | | The indexer reads every contracts trunk's definitions at its start, before it judges | a definition merged while it was down is never judged against a list that lacks it (ADR 0636 §1) | -| Another node's running build read through the node-engine seat's verb `haves` | ADR 0660 §2 reads the manifest of the build each node runs, and that node is its one owner (ADR 0596 §2) | -| What the gate owes the forge said as the event `owed`, consumed durably by the forge bridge, and answered by `groups` | ADR 0596 §1, §2 end the bridge's watch of the gate's keys; a durable consumer loses no act and performs none twice; one verb answers a group and what it owes | +| Another node's running build kept from the node-engine seat's `running` events by a durable consumer, the event carrying the fingerprint and the manifest's digest, the trunk's manifest for a node that said nothing | ADR 0660 §2 reads the manifest of the build each node runs; an event kept by a durable consumer answers while a node is away, where a call would wait on it; one fallback in the one function keeps the two parts agreed (ADR 0660 §3) | +| What the gate and the checker owe the forge said as the event `owed` per repository and pull request, each act safe to repeat at the forge, each outcome said as `owed.ended` and recorded, an unanswered act said again at the gate's start and stalled after 15 minutes | ADR 0596 §1, §2 end the watches of the gate's and the checker's keys; an act safe to repeat cannot be lost between the act and its record, where an id recorded first could; an outcome said and recorded leaves no act unseen | | A merge made at the forge opens a `forge-made` delivery record | its trace and outcomes are kept like every delivery's, and no later merge waits behind a merge the gate never heard | -| Every delivery part reaches the forge only through the seat `git`'s verbs | the delivery parts name no other module (ADR 0656 §1, acceptance test 27), and the forge's answers are typed in the seat's contract instead of guessed in each caller | -| A check that failed because a part of the mesh was not heard says `could not run`, naming the part, and is asked again on that part's `running` event | the operator sees a mesh fault apart from a failing change, and the check is asked again on the one event that can change its outcome, never on a timer (issue 1373) | +| Every delivery part and the gate reach the forge only through the seat `git`'s verbs and events, the act verbs granted by the holder's trusted `callers` to the forge bridge alone, a failed read answered as an error | the delivery parts name no other module (ADR 0656 §1, acceptance test 27); the forge's answers are typed in the seat's contract instead of guessed in each caller; the bridge stays the one part that acts on the forge; an error read as an empty answer would hide a forge down | +| A check that failed because a part of the mesh was not heard says `could not run`, naming the part, releases its claim, and is asked again once per heads and part on that part's `running` event, raising `check-could-not-run` after 60 minutes | the operator sees a mesh fault apart from a failing change, and the check is asked again on the one event that can change its outcome, never on a timer (issue 1373) | +| A module held for its claim raises `module-held` until it is composed | a module left out in silence would look delivered (the mesh's rule that failure is loud) | +| The indexer waits for the seat `git` at most 2 minutes at its start, then judges from its kept definitions and raises `view-waits` after five | longer than a forge's restart, and no start held behind a forge down for good | +| The forge before the forge bridge before the gate, reverted the other way | the verbs are served before they are called, and `owed` is said only to a bridge that hears it | +| The store seat's holder's own new build kept on its build node and fetched by `kept-artifact` when its seat is broken | its own build needs the store it replaces; a step of the delivery replaces the hand bootstrap | | 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. @@ -950,10 +1004,12 @@ the node-engine reads its want and the catalog half and never the assignment hal 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` and `owed`, -the checker's read of the gate's `source` and `turns`, the grants part's read of the assigner's state `wants`, the +the checker's read of the gate's `source` and `turns`, the forge module's watch of the checker's `owed` (its `mesh/check` +status, now the checker's `owed` event performed by the forge bridge) and its read of the gate's `source`, the grants +part's read of the assigner's state `wants`, the checker's validator request with no seat definitions, a seat list compiled into the node-engine, the checker, the builder or the grants part, a seat list's reader wired by a part itself, and a call of the forge module's own tools by -a delivery part; an old node-engine that wakes takes its new build in a form it reads. +a delivery part or the gate; an old node-engine that wakes takes its new build in a form it reads. ## Order without a person @@ -1030,6 +1086,19 @@ names go, since every new acting part needed code changes elsewhere (research 08 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 artifact store's holder takes its own new build even when its seat is the one broken.** A build module + stores what it builds through the seat `oci-artifact-registry` (or `container-image-registry` for an image), so the + holder of that seat is in its own build's path: its new build needs the store it replaces. As every holder's own + inputs are (above), that store is acted on by the running holder: the running holder stores its new build, and only + then is the new one wanted. When the running holder is what is broken (the seat refuses or does not answer a store, + as in the hand bootstrap of 2026-11-07 to 11-09), the build module keeps that one build on its own node among its + kept builds (*The bootstrap delivery*: `build_go_kept`), says in its step key that it is kept on that node, and the + builder's done key names it so; the node-engine composing the holder fetches it by its digest through the build + seat's verb `kept-artifact` of that node, and the new holder, once it runs, takes the store of every build after + it. This is the one artifact the mesh delivers from a build node instead of the store, and only for a holder of a + store seat; the design's choice under ADR 0656 §3 (a wait skips what the holder's own new version needs) and ADR + 0526 §2, whose build by a session's call it makes a step of the delivery instead of a hand act. How it is checked: + test 60. - **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. @@ -1120,12 +1189,15 @@ commit on the SDK's trunk or the head of an SDK pull request the forge holds, op delivers no verdict, so none of its builds is reused. A trunk move never ends a check: its verdict goes out of date when it ends, and is carried. A check that failed because a part of the mesh was not heard is still asked again when the nodes change, which judges heads no check judged. From research 085's milestone its verdict says - `could not run`, naming the part not heard, and it is asked again on that part's `running` event of the - node-engine seat (*What each step says*), never on a timer; a second check of the same heads asked while one runs, - or one erred, claims nothing and writes no status, by the claim above (issue 1373; ADR 0548 §1, ADR 0656 §5). How - it is checked: in the checker's whole-module test a check whose composer's node does not answer says `could not - run` naming it, and is asked again once a fixture `running` event of that node is said; a second ask of the same - heads writes no status. + `could not run`, naming the part not heard, and the check releases its claim on the verdict key, so the heads can + be judged again. The part's return is the one event that can change the outcome: the node-engine seat's + `running.` naming that part (a node waking, the forge module or a build module starting again alike), on which + the check is asked again, once per set of heads and per part returning, never on a timer; a `running` of the same + part at the same fingerprint after that asks nothing. A `could not run` verdict not asked again within 60 minutes + becomes the checker's finding `check-could-not-run`, naming the heads and the part, which the condition keeper + raises (ADR 0564 §2): an hour is the check's own bound. A second check of the same heads asked while one runs, or + one erred, claims nothing and writes no status, by the claim above (issue 1373; ADR 0548 §1, ADR 0656 §5). How it + is checked: test 58. - **A failed build node loses nothing.** An ask a build module took and never acknowledged goes to another node by the stream's own acknowledgement wait, with no timer of a part; a step's end is written on the build module's step key named by the ask's id (ADR 0525 §6). @@ -1191,14 +1263,15 @@ the check gates merges.* its record still to be written (below).* **Removed at research 085's milestone.** The hold, the merge turns, their stalled line, the gate's tools for them and -the setting `check-gates-merge` go in milestone G (the operator, on Telegram, 2026-11-09 01:11 UTC, choosing "Remove them -in G"). The setting has been false since 2026-10-25, so the gate wrote no turn and held no merge in that time, and the +the setting `check-gates-merge` go in milestone G, the gate's and the forge module's alike (the operator, on Telegram, +2026-11-09 01:11 UTC, choosing "Remove them in G"; the forge's merge then judges no check, *The seat `git`*). The setting has been false since 2026-10-25, so the gate wrote no turn and held no merge in that time, and the operator's word for G is that no check gates delivery (1921's note of 2026-11-07). From G's delivery an approved merge is handed to the forge bridge at once; a check running on a repository the merge goes into ends on the commit it read, and its verdict is carried (*A move of the trunk carries every verdict*); the checker reads nothing of the gate, and -the gate no longer watches the checker's verdicts for a hold. **No record carries this yet:** ADR 0548 §2 to §4, §9 -and §10 and ADR 0551 are in force, and a record that ends them is written before this design is merged; until it is -accepted, the rule below holds as those records have it. How it is checked: in the gate's whole-module test an approved merge into a +the gate no longer watches the checker's verdicts for a hold. **Its record is to be accepted before the push:** ADR +0548 §2 to §4, §9 and §10, ADR 0551 and ADR 0534 §4's judgement of `mesh/check` by the merge are in force, and the +record that ends the first two and changes the third (needs the operator's Approve) is to be accepted before milestone +G's pull requests are pushed; until it is accepted, the rule below holds as those records have it. How it is checked: in the gate's whole-module test an approved merge into a repository under a running check is handed at once, and the gate's manifest declares no `turns` and no setting `check-gates-merge`; in the checker's, a check claims at once with no gate state granted to read. @@ -1503,7 +1576,7 @@ pull request, and it owes the forge nothing. they are still the heads, their check passed against the trunk's head, and, while the check gates merges, no check of other heads runs on a repository the merge goes into (*Nothing merges into a repository under check*, above, which milestone G removes). From milestone G the forge bridge merges through the seat `git`'s verb - `merge-pull`, which keeps the merge tool's judgement of ADR 0534 §4. When the trunk moved, the merge waits for the carried verdict, and the event of its line asks the merge + `merge-pull`, which judges the Approve's heads and no check (*The seat `git`*, under *The verbs*). When the trunk moved, the merge waits for the carried verdict, and the event of its line asks the merge again; no timer asks it, no person rechecks, and the Approve is not asked again (ADR 0421 §3, §12, as ADR 0548 §8 changes them). The merge tool still refuses a verdict naming a trunk commit other than the trunk's head and asks nothing (ADR 0534 §4); the carry makes the verdict current. @@ -1888,8 +1961,9 @@ later change of a kept data's form, not as a case of this milestone. composes that module as left out, keeping its openings, says why in its have, and composes and applies every other module of the node; it never refuses the whole pass for one module's claim (ADR 0636 §3: a refused definition blocks new claims of that seat only). Met on 2026-11-08, when one claim problem refused a node's every module (issue 1966). - How it is checked: in the node-engine's composition table a node with one module whose claim is refused applies the - others, and that module's have names why. + A module held so is no silent leave-out: the node-engine writes the finding `module-held` naming the node, the + module and why, and the condition keeper keeps the condition (ADR 0564 §2) until the module is composed again, when + the node-engine clears the finding. How it is checked: test 55. - **The order a node takes to composing its own.** One delivery group carries every part: the seat `mesh-module-assigner` on the assigner, the controller's verb `placement-export` (as it is) and the controller's pin only, the node-engine (its record and its own composer), the vault's `own`, the grants part's reads, and mesh-config's reading of @@ -2112,8 +2186,8 @@ 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), and the acts the gate said it owes the forge for it, each with its id (*The delivery record*) | -| `owed` | an act the gate owes the forge (a merge, a status, the delivery's view, a comment, a line on the commit's note), with its pull request, head and id, said only from the gate's account and heard by the forge bridge through a durable consumer (*The delivery record*) | +| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656), and the acts the gate said it owes the forge for it, each with its id and its recorded outcome (*What the gate owes the forge*) | +| `owed` | an act the gate owes the forge (a merge, a status, the delivery's view, a comment, a line on the commit's note), with its pull request, head and id, on `mesh.seat.mesh-delivery.event.owed..`, said only from the gate's account and heard by the forge bridge through a durable consumer (*What the gate owes the forge*) | | `milestone.asked`, `milestone.withdrawn`, `milestone.answered` | the one Approve ask per milestone (ADR 0493 §9 as ADR 0659 §1 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*) | @@ -2122,39 +2196,80 @@ From research 085's milestone the seat gains: of the revision view, ADR 0656), and `entry` and `difference` on `mesh-wants` (ADR 0656), with the events of *What each step says*. -**The seat `node-engine`** gains, from research 085's milestone, the verb `haves`: what its node runs of each module, -the fingerprint and its phase, which the node-engine of another node and the grants part ask to decide a seat's pass -(ADR 0660 §2; *Who holds a seat is applied by one rule*), and the gate's `gate_record_repair` asks for a missing -outcome. It answers from the node's own haves, and every node's account may call it. +**The seat `node-engine`**'s event `running.` carries, from research 085's milestone, the module, its +fingerprint and the digest of its build's manifest, so the node-engine of another node and the grants part decide a +seat's pass from it (ADR 0660 §2; *Who holds a seat is applied by one rule*). No verb is added. **The seat `git`.** The seat `git` (design 26, *The git seat*), of the Delivery domain, defined in -`mesh-delivery-contracts`, holds no verb today, and the delivery parts reach the forge by naming the forge module's own -tools and guessing the shape of their answers (on 2026-11-09 one guess, an answer wrapped in a `result` field, made -316 conditions stand, issue 1966). From research 085's milestone the seat's definition carries the verbs below, their -inputs and answers typed in its contract, served by the module that claims the seat; every delivery part calls the -seat, never the forge module by name (acceptance test 27). The verbs are this design's choice under ADR 0596 §2 (a -module's data is asked through its seat verbs) and ADR 0625 §1 (a seat's contract is generated from its definition), -each for a call a delivery part makes of the forge today: +`mesh-delivery-contracts`, holds no verb today, and the delivery parts and the gate reach the forge by naming the forge +module's own tools and events and guessing the shape of their answers (on 2026-11-09 one guess, an answer wrapped in a +`result` field, made 316 conditions stand, issue 1966). From research 085's milestone the seat's definition carries the +verbs and events below, their inputs and answers typed in its contract, served and said by the module that claims the +seat; every delivery part and the gate call and hear the seat, never the forge module by name (acceptance test 27). +They are this design's choice under ADR 0596 §1, §2 (a module's events are subscribed to and its data asked through +its seat verbs) and ADR 0625 §1 (a seat's contract is generated from its definition). The table is built from what the +parts' manifests declare in `invokes` and `consumes` and what their code calls, read on main on 2026-11-09 (mesh-delivery +042b8d1f, mesh-catalog's gate); a declaration nothing calls (the checker's `gitea_commits`, `gitea_compare`, +`gitea_list_branches`) gets no verb and is dropped from its manifest: -| Verb | Answers or does | Called by | +| Verb | Answers or does | Called by (today's call) | |---|---|---| -| `pull` | one pull request, by its repository and number or by its merge commit: number, base, head branch, head, merge commit, author, state | the forge bridge (a merge the gate did not make), the gate's author of a stalled line (*The Approve and the merge*) | -| `open-pulls` | every open pull request of every repository, its head and base, the statuses its base requires and those its head has | the forge bridge, at its start and each reconnect (ADR 0443 §5) | -| `tags` | a repository's tags, paged, with the page's bound said when more exist | the forge bridge, for the builder's floor | -| `trees` | the trees of one commit | the forge bridge (a merged message's trees), the checker, the indexer (the definitions at its start) | -| `file` | one file at one commit | the checker, the indexer | -| `commits` | the commits between two commits of a repository | the checker | -| `branches` | a repository's branches with their heads | the checker | -| `contains` | whether one commit contains another | the indexer | -| `status`, `comment`, `view`, `note` | a commit status, a comment, the delivery's one view comment, a line on the commit's note under `refs/notes/mesh-plan` | the forge bridge alone, performing the gate's `owed` | -| `merge-pull` | merges a pull request at a head, judging what the forge module's merge tool judges today (ADR 0534 §4) | the forge bridge alone | +| `pull` | one pull request, by its repository and number or by its merge commit: number, base, head branch, head, merge commit, author, state | the forge bridge (a merge the gate did not make; `gitea_api`), the gate (a stalled line's author) | +| `open-pulls` | every open pull request of every repository, its head and base, the statuses its base requires and those its head has | the forge bridge at its start and each reconnect (ADR 0443 §5), the indexer, the gate (`gitea_open_pulls`) | +| `tags` | a repository's tags, paged, the page's bound said when more exist | the forge bridge, for the builder's floor (`gitea_api`) | +| `trees` | the trees of one commit | the forge bridge, the checker, the indexer (`gitea_commit_trees`) | +| `file` | one file at one commit | the forge bridge, the checker, the indexer (`gitea_get_file`) | +| `contains` | whether one commit contains another | the indexer, the gate (`gitea_contains`) | +| `statuses` | the statuses a commit carries | the gate (`gitea_commit_statuses`) | +| `status`, `comment`, `view`, `note` | a commit status, a comment, the delivery's one view comment, a line on the commit's note under `refs/notes/mesh-plan`; a comment and a note line carry the act's id and an id already held is skipped | the forge bridge alone, performing the gate's and the checker's `owed` (`gitea_commit_status`, `gitea_add_comment`, `gitea_delivery_view`, `gitea_note_append`) | +| `merge-pull` | merges a pull request at the head the operator's Approve names; refuses a head that moved, a branch no rule guards, a head the Approve does not name and a merge git refuses; judges no check (below); a pull request already merged at that head is answered performed | the forge bridge alone (`gitea_merge_pull_request`) | -The verbs that act are granted to the forge bridge's account alone, so the bridge stays the one part that acts on -the forge; the reads are granted to each part that declares them in its `invokes`. A verb's answer is the contract's -type, never a wrapper a caller unwraps. How it is checked: the seat definition's conformance fixtures hold one answer -of each verb, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's and the -indexer's unit fakes answer from the same fixtures; a call of a verb that acts from an account other than the forge -bridge's is refused by the bus; live, a search of the delivery parts' code finds no name of the forge module's tools. +| Event of the seat | Says | Heard by (today's event) | +|---|---|---| +| `pull.updated`, `pull.merged`, `pull.closed` | a pull request's new head, its merge with the merge commit, its close | the forge bridge, the gate (`gitea.pull.*`) | +| `forge.pull`, `forge.status`, `forge.delete` | the forge's own webhook of a pull request, of a commit status, of a deleted branch | the gate (`forge.pull`, `forge.status`), the forge bridge (`forge.delete`) (`gitea.forge.*`) | +| `repo.writable` | a repository the forge can write again | the gate (`gitea.repo.writable`) | + +Each is said on `mesh.seat.git.event.`, declared in the seat's definition, and heard through the hearer's +`consumes` of the seat. + +- **A failed read answers an error, never an empty answer.** When the forge does not answer, refuses or answers what + the contract cannot type, the verb answers an error naming why; an empty list or an absent file is answered only + when the forge said so. A caller never reads an error as "none". How it is checked: the seat's fixtures hold an + error answer for each verb, and each caller's unit table has a row for it (the forge bridge, the checker, the + indexer, the gate). +- **Who may call an act.** The holder's trusted config value `callers`, which today names per verb who may call the + two package seats' verbs (design 51), gains a line per verb of the seat `git`: the act verbs name the forge bridge + alone, the reads name each part that declares them in `invokes`; the grants part grants by it, and the holder + refuses a caller it does not name. The forge module's own acting tools (`gitea_merge_pull_request`, + `gitea_commit_status`, `gitea_delivery_view`, `gitea_note_append`, `gitea_add_comment`) retire with the milestone, + and so do its watch of the checker's state `owed` and its read of the gate's `source`, so the bridge stays the one + part that acts on the forge. How it is checked: test 51 reads the grants, not the code. +- **`merge-pull` judges no check.** The operator's answer that no check gates delivery (Telegram, 2026-11-09 01:11 UTC, + "Remove them in G") covers the forge module's own setting `check-gates-merge` as well as the gate's: both are + removed, and `merge-pull` merges an approved head whatever its `mesh/check` says. The record that ends ADR 0551 and + ADR 0548 §2 to §4, §9 and §10 also changes ADR 0534 §4's judgement of `mesh/check` by the merge; it is to be accepted + before the push (*Nothing merges into a repository under check*). +- **The order is the module graph's, and each step has its way back.** The verbs are a served-verb edge (ADR 0614): + the forge module's new build runs before the forge bridge's, and the forge bridge's before the gate's, since the + bridge calls the verbs and performs the gate's `owed`, and the gate says `owed` only to a bridge that hears it; all + three are ahead of the acting parts' steps, which read through the seat. While the new forge runs and the old bridge + still calls the retired acting tools, the old bridge's acts fail loudly and stay unperformed in the old gate's keys, + and the switch carries them (*What the gate owes the forge*). A revert goes back in the other order: the gate, then + the bridge, then the forge, each wanted only once the one after it in the forward order runs its reverted build, so + no part calls a verb nobody serves. +- **A part that cannot read the forge keeps what it has, and says so.** The indexer, at its start, waits for the + seat `git` at most 2 minutes; with no answer it judges from the last definitions it kept, says in its + `_first_views` that it waits for `git` and since when, writes `view-waits` after five minutes, and reads the + definitions once the seat answers. Two minutes is longer than a forge's restart, and short enough that a start is + not held behind a forge down for good. How it is checked: in the indexer's whole-module test with a fixture forge on + its old build, which serves no verb of the seat, the indexer starts, judges from its kept definitions, and raises + `view-waits` after five minutes. +- **How it is checked.** The seat definition's conformance fixtures hold one answer and one error of each verb and one + of each event, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's, the + indexer's and the gate's unit fakes answer from the same fixtures; a call of an act verb from an account other than + the forge bridge's is refused (test 51); live, a search of the delivery parts' and the gate's code finds no name of + the forge module's tools or events. **Links of the trace.** No part reads a review line (ADR 0659 §1 to §3); who may review and the review verb's grant stay issue 1782's, outside milestone G, and that verb gates nothing (ADR 0659 §2). The trace's links are this @@ -2447,24 +2562,53 @@ Each change ADR 0656 makes in an earlier record, and its test: What milestone G's prepare found, and its test: 50. **No description is read** (ADR 0659 §4). The Approve ask names the issues read from the milestone's links and a - stalled line the author read through the seat `git`, no notice of an `after:` line is said, and a test over the - gate's code finds no read of a description field (whole, gate). + stalled line the author read through the seat `git`'s `pull`, no notice of an `after:` line is said, and a test over + the gate's code finds no read of a description field (whole, gate). 51. **The forge is reached through its seat.** Every act the forge bridge performs and every read the forge bridge, - the checker and the indexer make of the forge goes through a verb of the seat `git`, each answer decoded as its - contract's type; an act called from an account other than the forge bridge's is refused; a search of the - delivery parts' code finds no name of the forge module's tools (whole, forge bridge; live). -52. **Nothing holds an approved merge for another check.** An approved merge into a repository under a running check - is handed at once; the gate declares no `turns` and no setting `check-gates-merge`, and the checker is granted no - read of the gate's state (whole, gate and checker; its record first, *Nothing merges into a repository under - check*). -53. **What the gate owes is said, and a forge-made merge is kept.** An `owed` said while the forge bridge is stopped - is performed once after its restart; a merge made at the forge opens a `forge-made` record and is taken in, and a - later merge in its repository follows it (whole, gate and forge bridge). + the checker, the indexer and the gate make of the forge goes through a verb of the seat `git`, and every forge + event they hear is the seat's; each answer is decoded as its contract's type and a failed read as an error; the + grants composed from the holder's `callers` give the act verbs to the forge bridge's account alone, a call of one + from another account is refused, and the forge module serves none of its retired acting tools (whole, forge bridge, + grants part; live, the grants read with `grants_bindings`). +52. **Nothing holds an approved merge for another check.** Once the record that ends ADR 0551 and ADR 0548 §2 to §4, + §9, §10 and changes ADR 0534 §4 is accepted (to be accepted before the push): an approved merge into a repository + under a running check is handed at once and merged whatever that check says; neither the gate nor the forge module + declares `check-gates-merge`, the gate declares no `turns`, and the checker is granted no read of the gate's state + (whole, gate, checker and forge module). +53. **What is owed is said, answered and kept.** An `owed` said while the forge bridge is stopped is performed once after + its restart; the bridge stopped between an act and its `owed.ended` repeats it and the forge holds it once; an + undecodable act is said `undecodable`; an act unanswered 15 minutes is listed by `stalled`; the switch with a merge + handed and not yet merged opens no `forge-made` record; a merge made at the forge opens one, said as `transition` + with why `forge-made`, and a later merge in its repository follows it; the checker's `mesh/check` is performed by + the bridge from the checker's `owed` (whole, gate and forge bridge). 54. **One reader of the seat list per part, and one view machinery.** Every part that reads the seat list has the two tools of ADR 0636 §7 from the runtime and no wiring of its own; every part that takes a first view answers its - held and awaited revision in `_first_views` (unit, mesh-sdk; live search). -55. **A claim problem holds one module.** A node with one module whose claim is refused applies the others, and that - module's have names why (unit, node-engine). + held and awaited revision in `_first_views`; the indexer with the forge on its old build, serving no verb of + the seat `git`, starts within 2 minutes on its kept definitions and raises `view-waits` after five (unit, mesh-sdk; + whole, indexer; live search). +55. **A claim problem holds one module, and says so.** A node with one module whose claim is refused applies the + others; that module's have names why, and `mesh-conditions.conditions` reads its `module-held` condition until the + module is composed again, when it clears (whole, node-engine and condition keeper). +56. **One rule picks a seat's holder, also with a node away** (ADR 0660 §1 to §3). With one node away whose last + `running` named a build that no longer claims the seat, and one node that has said nothing since the parts' + consumers began, the node-engine and the grants part each pick the same holder, the silent node counted by the + trunk's manifest; neither reads another node's have key, and neither calls a node (whole, node-engine and grants + part). +57. **The indexer reads every definition at its start.** Started with a definition on a contracts trunk that no event + announced, the indexer judges the first manifest claiming that seat with it, and answers it in + `seat-definitions` (whole, indexer). +58. **A check that could not run says so and is asked once more.** A check whose composer's node does not answer + reads `could not run` and names that node in `mesh-delivery.show`; the claim is released; the node's `running` asks + the check again once, and a second `running` of the same part at the same fingerprint asks no second check; a + `could not run` not asked again within 60 minutes raises `check-could-not-run` (whole, checker; live, read through + `mesh-delivery.show`). +59. **A session hears an accepted record** (issue 595). An Approve that accepts a record makes the knowledge base say + `reviewed` with what was accepted, by the operator and with the warrant, and the tool runner's account hears it, so + a session waiting with `mesh_wait` wakes on it (whole, knowledge base; live). +60. **The store's holder takes its own build when its seat is broken.** With a fixture holder of `oci-artifact-registry` + that refuses every store, its new build is kept on the build node, named in the builder's done key, fetched by the + node-engine through `kept-artifact`, and once it runs the next build is stored through the seat, with no hand act + (whole, builder, build module and node-engine). ## What retires @@ -2570,7 +2714,7 @@ release reports the controller's code lines against research 063's baseline (acc | L6 | Provisioning leaves the controller (ADR 0570): every provision is one key of the seat `mesh-provisioner`, held by mesh-provisioner, holding a reference and never a value, and each placed module's provisioning plan a key of its state `plans`, both composed from placement, the wants and the manifests on change events; each provider acts once all a provision depends on is provided; the assigner moves a want only once its build's provisions are provided; a provision no want needs is retiring, and its account or credential is removed only on the operator's Approve, its data never; the vault mints each provision's secret sealed to the consumer's and the provider's node, and the consumer's node-engine writes it; a wait for a person is a provision's state, which the watchman reads; bus permissions stay with the grants part (ADR 0349, ADR 0230, ADR 0547 §11) | — | mesh-sdk, the node-engine (`mesh-host`), mesh-tools (the tool runner's provisioner client), mesh-catalog (mesh-provisioner, the vault, the provisioner client, postgres, keycloak, gitea, route-adapter), mesh-delivery (assigner, watchman) | 1954 | | L7 | History and records leave the controller (ADR 0571): the builder keeps its build log, one entry at a build's start and one at its end, from which build history and durations are read and the module mesh-catalog asks the builds it missed; the artifact sweep is the seat `mesh-artifact-sweep`, held by artifact-store-tools beside the artifact store, its keep set from the store's own listing, the wants and the build log, sweeping at each build's end from the lease write and recording each deletion in the hand-act log; disk readings are disk-load's own state on every node; who holds which seat is the state `seat-holders` of the seat `mesh-placement` (now `mesh-module-assigner`), and tools are found by the tool runners' announcements; what a node runs is its want and its have; the grants part writes `grants..` 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: every check, a lone pull request's as a group of one and a verdict carried after a trunk move included, is judged by a checking program built from the change's own heads in the build seat's sandbox, which also makes the heads' one seat list that the check hands the manifest validator and the composer; it brings no seat definition and no manifest field, so the running checker checks it (ADR 0644; *The bootstrap delivery, and the one milestone after it*) | 26 needs it | mesh-sdk (`go/delivery`: the seat `node-build-go`'s ask `run` and the checker's record of which checking program judged; `go/composer`: the request's seat list), mesh-catalog (mesh-build-go: the run step, the refused answer, the kept programs' tools and sweep), mesh-delivery (checker: the checking program and its command), hq (this design, the glossary) | 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); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check | 26 to 55 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker, builder, grants part, gate's view, forge bridge, watchman), mesh-catalog (the gate, the build modules, the forge module, 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, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | +| 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); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check | 26 to 60 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker, builder, grants part, gate's view, forge bridge, watchman), mesh-catalog (the gate, the build modules, the forge module, 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, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | 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 @@ -2720,10 +2864,12 @@ B0's own choices, each checked by the rows of *How it is checked* below: it). No record decides a merge without the operator's Approve: ADR 0421 §1 and ADR 0398 §3 ask it for every merge, and the forge's merge tool merges only a head the operator approved (ADR 0534 §4). The shape this design would give it, once a record decides it: the verb `merge` of the seat `mesh-delivery`, taking the pull requests at their heads - and the reason, refused unless their check passed, opening a delivery record marked `recovery` with the caller as - the runtime proves it, and saying the event `recovery-merged`, which the teller tells the operator on every channel, - so a recovery merge cannot go untold. Until that record is accepted it is not built, and a recovery fix is asked the - operator's Approve like any change. + and the reason; callable only by the callers the gate's trusted config value `recovery-callers` names; refusing a + change that adds or widens an acting rule, a grant or a credential, which ADR 0656 §8 has named to the operator in + the Approve ask and which a recovery merge has none of; opening a delivery record marked `recovery` with the caller + as the runtime proves it; and saying the event `recovery-merged`, which the teller tells the operator on every + channel, so a recovery merge cannot go untold. It waits for its own record; until that record is accepted it is not + built, and a recovery fix is asked the operator's Approve like any change. - 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 @@ -2776,7 +2922,15 @@ in and recorded; the indexer reading every definition at its start; the seat `me contracts module; the seat `git`'s verbs for every call the delivery parts make of the forge; the seat list's reader wired once by the runtime; `go/views` as the view's machinery, with the seats' client packages typed over it; a check that could not run asked again on the missing part's `running`; and a claim problem holding one module. Its review by -architectural domain is still to come, and this section is rewritten with its findings. +architectural domain then ran, four domains side by side, and lead-47 ruled on round 1 (2026-11-09): a seat's pass is +decided from the node-engine seat's `running` events kept by durable consumers, with the trunk's manifest for a node +that said nothing, instead of a call to a node that may be away; every act owed to the forge is safe to repeat, its +outcome said and recorded, unanswered acts said again and stalled after 15 minutes, a group's merges said one after +another, and the switch carrying what is owed; the seat `git` covers every forge call and event of the delivery parts +and the gate, its act verbs granted by the holder's trusted `callers`, the forge's acting tools and its watch of the +checker retired, and `merge-pull` judging no check, its record to be accepted before the push; the order forge, bridge, +gate with its way back, and the indexer's start without the forge; `module-held`, an error for a failed forge read and +the re-ask of a check that could not run; numbered tests 56 to 60; and the store seat's holder's own build. 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 -- 2.54.0 From 4f388e845294f148738856605e352caa8696ac1f Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 13:14:19 +0100 Subject: [PATCH 3/8] Take in round 2 of design 47's G review: running kept per subject, the merge waits on the Approve alone, ADR 0661 named Round 2 found the pass's running events could age out of a new consumer's reach, the merge bullet still holding for a carry, the checker's own owed acts unanswered, findings that never cleared, and kept-artifact open to any module and caller. Each is now written as the lead ruled, with its test row. Claude-Session: https://claude.ai/code/session_01NcbgfFfQ4Z9UE5jK32PmE5 --- .../47-delivery-from-commit-to-delivered.md | 119 +++++++++++------- 1 file changed, 75 insertions(+), 44 deletions(-) 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 07f1605e..42318e0d 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 @@ -4,6 +4,7 @@ status: in-progress code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-catalog modules/gitea, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, mesh-contracts, hq 00-META/checks] updated: 2026-11-09 decisions: + - "ADR 0661" - "ADR 0660" - "ADR 0659" - "ADR 0656" @@ -542,7 +543,9 @@ checker's `mesh/check` from a watch of the checker's state `owed`: reads of anot performed` (the forge did not answer within the call's bound, with the error) or `undecodable` (an act it cannot read, with why): an act it cannot decode is said, never only logged. The gate hears the stream `forge` through its durable consumer, writes the outcome into the delivery record, and says an act's id is answered only once an - outcome is recorded. + outcome is recorded. The checker does the same for its own acts: it hears `owed.ended` for its own ids, records each + outcome in its own state (it watches only its own state), says again at its start, under the same id, every act of + its own still unanswered, and lists one unanswered past 15 minutes in its own stalled read. - **What is unanswered is said again, and stalls by a bound.** At its start the gate says again, under the same id, every act its records hold unanswered. An act unanswered 15 minutes after it was said is listed by `stalled`, which the condition keeper raises as the stalled line (ADR 0421 §7, ADR 0564 §2): fifteen minutes is many times a @@ -778,8 +781,11 @@ Findings 11). How each is kept, read and retained is this design's choice under - **Who holds a seat is applied by one rule, from what each node runs.** The node-engine and the grants part decide a seat's pass by mesh-sdk's `SeatsToPassOn` (ADR 0660 §3), reading the manifest of the build each assignment runs (ADR 0660 §2). What each node runs is heard, never asked: each part keeps it from the node-engine seat's events - `running.` (*What each step says*), through a durable consumer of its own declared in its `consumes`, so a - pass is decided with no call to a node that may be away. The event carries the module, the fingerprint and the + `running..` (*What each step says*), through a durable consumer of its own declared in its `consumes`, + so a pass is decided with no call to a node that may be away. The seat's stream keeps the last message per subject + with no age limit, so the last `running` of every module on every node stays on it however long ago it was said, + and each part's durable consumer starts from the last message per subject, never from now: a consumer made after a + node's last `running`, and two parts' consumers made at different times, hold the same builds. The event carries the module, the fingerprint and the digest of the build's manifest, which is the indexer's manifest of that build (ADR 0660 §2's "the indexer's manifest of it"), read by digest from the catalog half; the builder's done key is not needed, since the event names the digest it would give. A node that has said no `running` for a module since the part's consumer began counts by @@ -944,7 +950,7 @@ say another runs a build (ADR 0363 §2); the gate's events are granted to the ga | 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-delivery-contracts` (ADR 0641 §2) | -| an entry composed, and running healthy | `composed`, `running` | `node-engine`, the node-engine on each node | `mesh.seat.node-engine.event.composed.`, `.running.` | the seat's definition in `mesh-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 latter on a stream kept last per subject with no age limit | the seat's definition in `mesh-contracts` | | a node's capabilities changed | `capabilities-changed` | `node-engine`, the node-engine on that node | `mesh.seat.node-engine.event.capabilities-changed.` | 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 | @@ -1064,7 +1070,7 @@ names go, since every new acting part needed code changes elsewhere (research 08 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 + 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, @@ -1087,16 +1093,27 @@ names go, since every new acting part needed code changes elsewhere (research 08 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 artifact store's holder takes its own new build even when its seat is the one broken.** A build module - stores what it builds through the seat `oci-artifact-registry` (or `container-image-registry` for an image), so the + stores the artifacts it builds through the seat `oci-artifact-registry`, so the holder of that seat is in its own build's path: its new build needs the store it replaces. As every holder's own inputs are (above), that store is acted on by the running holder: the running holder stores its new build, and only then is the new one wanted. When the running holder is what is broken (the seat refuses or does not answer a store, as in the hand bootstrap of 2026-11-07 to 11-09), the build module keeps that one build on its own node among its kept builds (*The bootstrap delivery*: `build_go_kept`), says in its step key that it is kept on that node, and the - builder's done key names it so; the node-engine composing the holder fetches it by its digest through the build - seat's verb `kept-artifact` of that node, and the new holder, once it runs, takes the store of every build after - it. This is the one artifact the mesh delivers from a build node instead of the store, and only for a holder of a - store seat; the design's choice under ADR 0656 §3 (a wait skips what the holder's own new version needs) and ADR + builder's done key names it so with its digest; the node-engine composing the holder fetches it through the build + seat's verb `kept-artifact` of that node, and the new holder, once it runs, takes the store of every build after it. + - The verb sits in the build seats' definitions (`node-build-go`, `node-build-node`, `node-build-image`, in + `mesh-delivery-contracts`), and its callers are the node-engines alone: the definition names them, and the grants + part grants it to no other account. + - It answers only for a module that claims the seat `oci-artifact-registry`, and refuses every other, naming the + module, so a store outage never turns into delivering from build nodes. + - The bytes go as an object by digest on the bus (the old `objects` form, ADR 0619 §2), never in a value, since an + artifact is over the bus's value limit; the node-engine checks the bytes it fetched against the digest in the + builder's done key and applies nothing that differs. + - While the keeping node is away, or its sweep removed the build, the want waits, and the node-engine says in its + have that it waits for that node or for a build no longer kept. + + This is the one artifact the mesh delivers from a build node instead of the store, and only for the holder of + `oci-artifact-registry`; the design's choice under ADR 0656 §3 (a wait skips what the holder's own new version needs) and ADR 0526 §2, whose build by a session's call it makes a step of the delivery instead of a hand act. How it is checked: test 60. - **The check refuses a cycle among the waits within one step** (ADR 0656, which changes ADR 0603 §2's refusal of a @@ -1191,12 +1208,13 @@ commit on the SDK's trunk or the head of an SDK pull request the forge holds, op again when the nodes change, which judges heads no check judged. From research 085's milestone its verdict says `could not run`, naming the part not heard, and the check releases its claim on the verdict key, so the heads can be judged again. The part's return is the one event that can change the outcome: the node-engine seat's - `running.` naming that part (a node waking, the forge module or a build module starting again alike), on which + `running..` naming that part (a node waking, the forge module or a build module starting again alike), on which the check is asked again, once per set of heads and per part returning, never on a timer; a `running` of the same part at the same fingerprint after that asks nothing. A `could not run` verdict not asked again within 60 minutes becomes the checker's finding `check-could-not-run`, naming the heads and the part, which the condition keeper - raises (ADR 0564 §2): an hour is the check's own bound. A second check of the same heads asked while one runs, or - one erred, claims nothing and writes no status, by the claim above (issue 1373; ADR 0548 §1, ADR 0656 §5). How it + raises (ADR 0564 §2): an hour is the check's own bound. The finding clears when the heads are checked again or + replaced, or their pull request closes. A second check of the same heads asked while one runs, or after one that + erred without releasing its claim, claims nothing and writes no status, by the claim above (issue 1373; ADR 0548 §1, ADR 0656 §5). How it is checked: test 58. - **A failed build node loses nothing.** An ask a build module took and never acknowledged goes to another node by the stream's own acknowledgement wait, with no timer of a part; a step's end is written on the build module's step key named @@ -1259,8 +1277,8 @@ the check gates merges.* ### Nothing merges into a repository under check -*ADR 0548 §2 to §4, §9, §10, as ADR 0551 bounds them; from research 085's milestone removed, by the operator's word, -its record still to be written (below).* +*ADR 0548 §2 to §4, §9, §10, as ADR 0551 bounds them; from research 085's milestone removed by ADR 0661 (proposed, +needs the operator's Approve; below).* **Removed at research 085's milestone.** The hold, the merge turns, their stalled line, the gate's tools for them and the setting `check-gates-merge` go in milestone G, the gate's and the forge module's alike (the operator, on Telegram, @@ -1269,9 +1287,9 @@ operator's word for G is that no check gates delivery (1921's note of 2026-11-07 is handed to the forge bridge at once; a check running on a repository the merge goes into ends on the commit it read, and its verdict is carried (*A move of the trunk carries every verdict*); the checker reads nothing of the gate, and the gate no longer watches the checker's verdicts for a hold. **Its record is to be accepted before the push:** ADR -0548 §2 to §4, §9 and §10, ADR 0551 and ADR 0534 §4's judgement of `mesh/check` by the merge are in force, and the -record that ends the first two and changes the third (needs the operator's Approve) is to be accepted before milestone -G's pull requests are pushed; until it is accepted, the rule below holds as those records have it. How it is checked: in the gate's whole-module test an approved merge into a +0548 §2 to §4, §9 and §10, ADR 0551 and ADR 0534 §4's judgement of `mesh/check` by the merge are in force, and ADR +0661, which ends the first two and changes the third (proposed, needs the operator's Approve), is to be accepted before +milestone G's pull requests are pushed; until it is accepted, the rule below holds as those records have it. How it is checked: in the gate's whole-module test an approved merge into a repository under a running check is handed at once, and the gate's manifest declares no `turns` and no setting `check-gates-merge`; in the checker's, a check claims at once with no gate state granted to read. @@ -1575,11 +1593,13 @@ pull request, and it owes the forge nothing. - **The merge.** mesh-delivery merges through the forge's merge tool at the heads the Approve names, only while they are still the heads, their check passed against the trunk's head, and, while the check gates merges, no check of other heads runs on a repository the merge goes into (*Nothing merges into a repository under check*, - above, which milestone G removes). From milestone G the forge bridge merges through the seat `git`'s verb - `merge-pull`, which judges the Approve's heads and no check (*The seat `git`*, under *The verbs*). When the trunk moved, the merge waits for the carried verdict, and the event of its line asks the merge - again; no timer asks it, no person rechecks, and the Approve is not asked again (ADR 0421 §3, §12, as ADR 0548 - §8 changes them). The merge tool still refuses a verdict naming a trunk commit other than the trunk's head and - asks nothing (ADR 0534 §4); the carry makes the verdict current. + above, which milestone G removes). Until milestone G, when the trunk moved, the merge waits for the carried + verdict, and the event of its line asks the merge again; no timer asks it, no person rechecks, and the Approve is + not asked again (ADR 0421 §3, §12, as ADR 0548 §8 changes them); and the merge tool refuses a verdict naming a trunk + commit other than the trunk's head and asks nothing (ADR 0534 §4), the carry making the verdict current. From + milestone G the merge waits on nothing but the Approve's heads and a trunk that takes no push: the forge bridge + merges through the seat `git`'s verb `merge-pull`, which judges those two and no check, and a trunk move holds no + merge for its carry (ADR 0661, to be accepted before the push; *The seat `git`*, under *The verbs*). - **A push or a Decline ends the Approve.** A new head ends its ask or its Approve, for a group every member's, and is said once on the pull request; the new head is asked again once its checks pass. A Decline merges nothing and is said once (ADR 0421 §6). @@ -1962,8 +1982,8 @@ later change of a kept data's form, not as a case of this milestone. module of the node; it never refuses the whole pass for one module's claim (ADR 0636 §3: a refused definition blocks new claims of that seat only). Met on 2026-11-08, when one claim problem refused a node's every module (issue 1966). A module held so is no silent leave-out: the node-engine writes the finding `module-held` naming the node, the - module and why, and the condition keeper keeps the condition (ADR 0564 §2) until the module is composed again, when - the node-engine clears the finding. How it is checked: test 55. + module and why, and the condition keeper keeps the condition (ADR 0564 §2) until the module is composed again or is + no longer wanted on that node, when the node-engine clears the finding. How it is checked: test 55. - **The order a node takes to composing its own.** One delivery group carries every part: the seat `mesh-module-assigner` on the assigner, the controller's verb `placement-export` (as it is) and the controller's pin only, the node-engine (its record and its own composer), the vault's `own`, the grants part's reads, and mesh-config's reading of @@ -2196,7 +2216,7 @@ From research 085's milestone the seat gains: of the revision view, ADR 0656), and `entry` and `difference` on `mesh-wants` (ADR 0656), with the events of *What each step says*. -**The seat `node-engine`**'s event `running.` carries, from research 085's milestone, the module, its +**The seat `node-engine`**'s event `running..` carries, from research 085's milestone, the module, its fingerprint and the digest of its build's manifest, so the node-engine of another node and the grants part decide a seat's pass from it (ADR 0660 §2; *Who holds a seat is applied by one rule*). No verb is added. @@ -2257,14 +2277,15 @@ Each is said on `mesh.seat.git.event.`, declared in the seat's definition still calls the retired acting tools, the old bridge's acts fail loudly and stay unperformed in the old gate's keys, and the switch carries them (*What the gate owes the forge*). A revert goes back in the other order: the gate, then the bridge, then the forge, each wanted only once the one after it in the forward order runs its reverted build, so - no part calls a verb nobody serves. + no part calls a verb nobody serves. ADR 0656 §3 stands beside it: the acting parts' new versions act first in their + own steps. Two things cover the gap while the forge is still on its old build and serves no verb of the seat: the + indexer starts on its kept definitions (below), and a check whose read through the seat `git` fails says `could not + run`, naming the forge, and is asked again on the forge module's `running` (*A check runs to its end*). - **A part that cannot read the forge keeps what it has, and says so.** The indexer, at its start, waits for the seat `git` at most 2 minutes; with no answer it judges from the last definitions it kept, says in its `_first_views` that it waits for `git` and since when, writes `view-waits` after five minutes, and reads the - definitions once the seat answers. Two minutes is longer than a forge's restart, and short enough that a start is - not held behind a forge down for good. How it is checked: in the indexer's whole-module test with a fixture forge on - its old build, which serves no verb of the seat, the indexer starts, judges from its kept definitions, and raises - `view-waits` after five minutes. + definitions once the seat answers; it then judges again every manifest it judged from kept definitions. Two minutes is longer than a forge's restart, and short enough that a start is + not held behind a forge down for good. How it is checked: test 54. - **How it is checked.** The seat definition's conformance fixtures hold one answer and one error of each verb and one of each event, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's, the indexer's and the gate's unit fakes answer from the same fixtures; a call of an act verb from an account other than @@ -2570,9 +2591,10 @@ What milestone G's prepare found, and its test: grants composed from the holder's `callers` give the act verbs to the forge bridge's account alone, a call of one from another account is refused, and the forge module serves none of its retired acting tools (whole, forge bridge, grants part; live, the grants read with `grants_bindings`). -52. **Nothing holds an approved merge for another check.** Once the record that ends ADR 0551 and ADR 0548 §2 to §4, - §9, §10 and changes ADR 0534 §4 is accepted (to be accepted before the push): an approved merge into a repository - under a running check is handed at once and merged whatever that check says; neither the gate nor the forge module +52. **Nothing holds an approved merge for another check.** Once ADR 0661 (proposed, needs the operator's Approve; to + be accepted before the push), which ends ADR 0551 and ADR 0548 §2 to §4, §9, §10 and changes ADR 0534 §4, is + accepted: an approved merge into a repository under a running check is handed at once and merged whatever that + check says; a merge after a trunk move is not held for the carry; neither the gate nor the forge module declares `check-gates-merge`, the gate declares no `turns`, and the checker is granted no read of the gate's state (whole, gate, checker and forge module). 53. **What is owed is said, answered and kept.** An `owed` said while the forge bridge is stopped is performed once after @@ -2580,35 +2602,44 @@ What milestone G's prepare found, and its test: undecodable act is said `undecodable`; an act unanswered 15 minutes is listed by `stalled`; the switch with a merge handed and not yet merged opens no `forge-made` record; a merge made at the forge opens one, said as `transition` with why `forge-made`, and a later merge in its repository follows it; the checker's `mesh/check` is performed by - the bridge from the checker's `owed` (whole, gate and forge bridge). + the bridge from the checker's `owed`, and a checker status the forge refuses is recorded in the checker's own + state and listed in its stalled read (whole, gate, checker and forge bridge). 54. **One reader of the seat list per part, and one view machinery.** Every part that reads the seat list has the two tools of ADR 0636 §7 from the runtime and no wiring of its own; every part that takes a first view answers its - held and awaited revision in `_first_views`; the indexer with the forge on its old build, serving no verb of - the seat `git`, starts within 2 minutes on its kept definitions and raises `view-waits` after five (unit, mesh-sdk; - whole, indexer; live search). + held and awaited revision in `_first_views`. With the forge on its old build, serving no verb of the seat + `git`: the indexer starts within 2 minutes on its kept definitions, raises `view-waits` after five, and once the + seat answers judges again every manifest it judged from kept definitions; a check whose read through the seat + fails says `could not run` naming the forge, and is asked again on the forge module's `running` (unit, mesh-sdk; + whole, indexer and checker; live search). 55. **A claim problem holds one module, and says so.** A node with one module whose claim is refused applies the others; that module's have names why, and `mesh-conditions.conditions` reads its `module-held` condition until the - module is composed again, when it clears (whole, node-engine and condition keeper). + module is composed again or no longer wanted on that node, when it clears (whole, node-engine and condition + keeper). 56. **One rule picks a seat's holder, also with a node away** (ADR 0660 §1 to §3). With one node away whose last `running` named a build that no longer claims the seat, and one node that has said nothing since the parts' consumers began, the node-engine and the grants part each pick the same holder, the silent node counted by the - trunk's manifest; neither reads another node's have key, and neither calls a node (whole, node-engine and grants - part). + trunk's manifest; a consumer made after a node's last `running`, and the two parts' consumers made at different + times, still apply that build's manifest and pick the same holder; neither reads another node's have key, and + neither calls a node (whole, node-engine and grants part). 57. **The indexer reads every definition at its start.** Started with a definition on a contracts trunk that no event announced, the indexer judges the first manifest claiming that seat with it, and answers it in `seat-definitions` (whole, indexer). 58. **A check that could not run says so and is asked once more.** A check whose composer's node does not answer reads `could not run` and names that node in `mesh-delivery.show`; the claim is released; the node's `running` asks the check again once, and a second `running` of the same part at the same fingerprint asks no second check; a - `could not run` not asked again within 60 minutes raises `check-could-not-run` (whole, checker; live, read through + `could not run` not asked again within 60 minutes raises `check-could-not-run`, which reads cleared once the heads + are checked again, replaced, or their pull request closes (whole, checker; live, read through `mesh-delivery.show`). 59. **A session hears an accepted record** (issue 595). An Approve that accepts a record makes the knowledge base say `reviewed` with what was accepted, by the operator and with the warrant, and the tool runner's account hears it, so a session waiting with `mesh_wait` wakes on it (whole, knowledge base; live). 60. **The store's holder takes its own build when its seat is broken.** With a fixture holder of `oci-artifact-registry` that refuses every store, its new build is kept on the build node, named in the builder's done key, fetched by the - node-engine through `kept-artifact`, and once it runs the next build is stored through the seat, with no hand act - (whole, builder, build module and node-engine). + node-engine through `kept-artifact`, and once it runs the next build is stored through the seat, with no hand act; + a call of `kept-artifact` from an account other than a node-engine's is refused; it is refused for a module that + does not claim `oci-artifact-registry`; fetched bytes that differ from the done key's digest are not applied; an + artifact over the bus's value limit is fetched whole as an object; with the keeping node away, or the build swept, + the want waits and the have says why (whole, builder, build module and node-engine). ## What retires -- 2.54.0 From e73a1a7bcf7a968050d4e4aa1d989ac358866a3f Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 19:56:42 +0100 Subject: [PATCH 4/8] Take in round 3 of design 47's G review: no module reads the indexer's modules, a put ends itself, ADR 0662 withdrawn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit G stops declaring the indexer's modules, so the eight other modules that read it would break unless they move to the catalog half in the same milestone. A put without hold never ended and pinned its want; ADR 0546 §5 already says it ends once the node runs it, now heard on the node-engine seat's running event. ADR 0662 decided ADR 0661's switch another way and is withdrawn, so G builds ADR 0661 alone. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../47-delivery-from-commit-to-delivered.md | 55 +++++++++++++++++-- 1 file changed, 49 insertions(+), 6 deletions(-) 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 42318e0d..193a80ef 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 @@ -47,6 +47,7 @@ decisions: - "ADR 0549" - "ADR 0548" - "ADR 0547" + - "ADR 0546" - "ADR 0539" - "ADR 0536" - "ADR 0534" @@ -617,7 +618,7 @@ module's progress on each node is its want against its have, shown by the view ( ## 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, +ADR 0596, ADR 0603 §2, §6, §7, ADR 0615 §1, §2, 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, @@ -707,7 +708,7 @@ Findings 11). How each is kept, read and retained is this design's choice under | 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 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, nor its tool `indexer_read_modules` or its view, every reader of it reading the catalog half (*Every other reader of which modules exist*, above) | 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 | @@ -768,6 +769,19 @@ Findings 11). How each is kept, read and retained is this design's choice under candidate's view is read only by the parts that judge a candidate, inside the check its manifest validator and composer, and the indexer's candidate pass (ADR 0656, which changes ADR 0636 §4 in that the candidate pass joins them). +- **Every other reader of which modules exist reads the catalog half, in the same milestone.** From milestone G the + indexer declares no state `modules` and serves neither its tool `indexer_read_modules` nor a view of it (*Reverted*, + below), so every module that learns from it which modules exist moves in milestone G, not after: the watchman (its + manifest findings), the status view (`view_reach`), the forge module (which repositories its account writes), the + holders of the seats `mesh-config`, `mesh-provisioner` and `mesh-self-check`, the claude-code module and the tool + runner. Each takes a first view of the catalog half by the seat `mesh-catalog`'s verb `revision` and follows its + event `catalog-revision`, through `meshcatalogclient` in Go or the seat's generated TypeScript contract in the forge + module, names the seat and never the indexer, and keeps no list of its own (ADR 0656 §6, ADR 0596 §2; ADR 0615 §1, + under which every reader of the indexer's state moves in one milestone). The order is the module graph's: each + declares the verb in its `invokes`, so the served-verb edge delivers none of them to a node before an indexer serving + `revision` runs there (ADR 0615 §2, ADR 0614). The design's choice under ADR 0656 §6: one form of the fact, never + `modules` kept beside the catalog graph for these readers, which would be a second source of the same fact. How it is + checked: test 61. - **The trunk's heads come from the catalog half.** The checker takes the trunk heads a check merges onto from the catalog half, which carries the heads, and never reads the gate's state `source` (ADR 0596 §2); with the hold and the turns removed it reads nothing of the gate at all (*Nothing merges into a repository under check*). @@ -1012,7 +1026,8 @@ code finds none of the reads the revision view replaces, each by name: the index the checker's watch of the indexer's `candidates.`, the forge bridge's read of the gate's `merges` and `owed`, the checker's read of the gate's `source` and `turns`, the forge module's watch of the checker's `owed` (its `mesh/check` status, now the checker's `owed` event performed by the forge bridge) and its read of the gate's `source`, the grants -part's read of the assigner's state `wants`, the +part's read of the assigner's state `wants`, any module's read of the indexer's `modules` (its tool +`indexer_read_modules` in an `invokes`, its view in a `consumes`), the checker's validator request with no seat definitions, a seat list compiled into the node-engine, the checker, the builder or the grants part, a seat list's reader wired by a part itself, and a call of the forge module's own tools by a delivery part or the gate; an old node-engine that wakes takes its new build in a form it reads. @@ -1289,7 +1304,8 @@ and its verdict is carried (*A move of the trunk carries every verdict*); the ch the gate no longer watches the checker's verdicts for a hold. **Its record is to be accepted before the push:** ADR 0548 §2 to §4, §9 and §10, ADR 0551 and ADR 0534 §4's judgement of `mesh/check` by the merge are in force, and ADR 0661, which ends the first two and changes the third (proposed, needs the operator's Approve), is to be accepted before -milestone G's pull requests are pushed; until it is accepted, the rule below holds as those records have it. How it is checked: in the gate's whole-module test an approved merge into a +milestone G's pull requests are pushed; until it is accepted, the rule below holds as those records have it. ADR 0662 +is withdrawn; G builds ADR 0661. How it is checked: in the gate's whole-module test an approved merge into a repository under a running check is handed at once, and the gate's manifest declares no `turns` and no setting `check-gates-merge`; in the checker's, a check claims at once with no gate state granted to read. @@ -1860,7 +1876,7 @@ later change of a kept data's form, not as a case of this milestone. ## 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.* +*ADR 0360 §8, §9, ADR 0377, ADR 0429 §4, §6, ADR 0539 §9, §14 to §16, ADR 0546 §5, §6, ADR 0547 §2 to §9, ADR 0549, ADR 0555.* - **Each node composes its own.** For each want `want..` the node agent composes that module for its own node with the composition of `mesh-delivery-sdk` from the manifest by digest the indexer wrote, the module's resolved @@ -2036,6 +2052,18 @@ later change of a kept data's form, not as a case of this milestone. fingerprint, the digest, the phase and the health after the module's start window. A failed apply is loud, retried by the next change of the want or the composition, never by a clock, and fixed forward (ADR 0360 §8, ADR 0539 §9). +- **A put without hold ends itself once the node runs it.** A put the assigner's tool `assigner_put` writes with + `hold` false ends when the node's have runs its fingerprint healthy, and the assigner's rules write the want again + from their next pass (ADR 0546 §5); a held put stays until `assigner_release` (ADR 0546 §6). The assigner hears that + the node runs it as the node-engine seat's `running..` naming the put's fingerprint, through a durable + consumer of its own declared in its `consumes`, the one its composing waits hear too (*Composing waits per node*), + so a put asked while the node was away ends once the node says it runs it; it never reads the node's have across + the seam (ADR 0596 §2). On that event it marks the put ended, says the step as its event `put`, and from then on its + rules own the want; a `running` naming another fingerprint ends nothing. The design's choice under ADR 0546 §5: the + event the node already says is the one fact that ends the put, so a put never pins a want by being forgotten, as + lead-47's puts of a node-engine build did for seven hours on 2026-11-09 (issue 1966), and milestone G's one logged + put of its own indexer ends by itself. Built first in the issue 1966 recovery, kept whole in milestone G. How it is + checked: test 62. - **The bus module is wanted like any module; only a warranted want replaces its server** (ADR 0594, changing ADR 0539 §14 and §15, ADR 0546 §8 and ADR 0565 §7 for the bus module). The assigner writes the bus module's want by the same rules as every module's, and the node agent applies what that want brings (its files, its processes' bus @@ -2640,6 +2668,17 @@ What milestone G's prepare found, and its test: does not claim `oci-artifact-registry`; fetched bytes that differ from the done key's digest are not applied; an artifact over the bus's value limit is fetched whole as an object; with the keeping node away, or the build swept, the want waits and the have says why (whole, builder, build module and node-engine). +61. **No module reads the indexer's `modules`.** The watchman's manifest findings, the status view's `view_reach`, the + forge module's writable repositories and the reads of the holders of `mesh-config`, `mesh-provisioner` and + `mesh-self-check`, of the claude-code module and of the tool runner each answer from the catalog half: after a + merge that adds a module, each names it once `catalog-revision` is said, with no call of the indexer by name; the + indexer's manifest declares no `modules` (each module's existing tests, a row each; live, a search of the catalog's + and mesh-tools' code and manifests finds no `indexer_read_modules` and no view `modules`). +62. **A put without hold ends itself.** A put with `hold` false of a fingerprint the node does not run yet ends on that + node's `running` naming its fingerprint, says the step `put`, and the next pass of the rules writes the want, so a + later build is wanted with no `assigner_release`; a `running` of another fingerprint ends nothing; a held put stays + until released; a put whose `running` was said while the assigner was stopped ends after its restart (whole, + assigner; live, `assigner_puts` after milestone G's put of its own indexer). ## What retires @@ -2961,7 +3000,11 @@ another, and the switch carrying what is owed; the seat `git` covers every forge and the gate, its act verbs granted by the holder's trusted `callers`, the forge's acting tools and its watch of the checker retired, and `merge-pull` judging no check, its record to be accepted before the push; the order forge, bridge, gate with its way back, and the indexer's start without the forge; `module-held`, an error for a failed forge read and -the re-ask of a check that could not run; numbered tests 56 to 60; and the store seat's holder's own build. +the re-ask of a check that could not run; numbered tests 56 to 60; and the store seat's holder's own build. Round 3 +takes in three findings of G's second prepare refresh, as lead-47 ruled (2026-11-09): every other module that read the +indexer's `modules` reads the catalog half in milestone G (*The revision view*, *Reverted*; test 61); a put without +hold ends itself on the node's `running`, built first in the issue 1966 recovery (*Compositions, wants and the apply*; +test 62); and ADR 0662, which decided the same switch as ADR 0661 another way, is withdrawn, so G builds ADR 0661. 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 -- 2.54.0 From 205992f15c5dca4ef041a5a9acc490e28bd9ea91 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 20:02:31 +0100 Subject: [PATCH 5/8] Take in round 3's review of design 47's G amendment: the put's end by step, the readers' order and waits The reviewers found the put's end written as built when the recovery reads the have, no end for a put of the running build, nothing said for a put that stands, two of the eight readers outside the served-verb order, the readers missing the no-view rule and the candidate case, and a revert's restored modules unsaid. Each is now written as the lead ruled, with rows in tests 61 and 62. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../47-delivery-from-commit-to-delivered.md | 99 ++++++++++++++----- 1 file changed, 73 insertions(+), 26 deletions(-) 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 193a80ef..295414cd 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 @@ -19,6 +19,7 @@ decisions: - "ADR 0633" - "ADR 0632" - "ADR 0625" + - "ADR 0621" - "ADR 0619" - "ADR 0618" - "ADR 0615" @@ -618,7 +619,7 @@ module's progress on each node is its want against its have, shown by the view ( ## 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 0615 §1, §2, ADR 0567 §5, ADR 0571 §8, ADR 0614 §4, ADR 0618 §2, ADR 0619 §1 to §3, ADR 0636 §1, +ADR 0596, ADR 0603 §2, §6, §7, ADR 0615 §1 to §6, ADR 0621 §1, §2, ADR 0614 §3, 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, @@ -708,7 +709,7 @@ Findings 11). How each is kept, read and retained is this design's choice under | 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, nor its tool `indexer_read_modules` or its view, every reader of it reading the catalog half (*Every other reader of which modules exist*, above) | yes: `modules` as it stood at G's delivery, and the consumer's position | + | 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, nor its tool `indexer_read_modules` or its view, every reader of it reading the catalog half (*Every other reader of which modules exist*, under *The revision view*, below) | 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 | @@ -717,7 +718,11 @@ Findings 11). How each is kept, read and retained is this design's choice under 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. + set back. The restored `modules` is as it stood at G's delivery, so a module added since is missing from it until + the reverted indexer takes in its next source revision, and a reverted reader that learns which modules exist from + `modules` does not name it until then. What a reader granted since stays: the forge module's write grants on a + repository it learnt of from the catalog half are kept, since it grants writes and takes none away, so a revert + costs no repository its account's writes. ### A change's graph difference is its delivery @@ -771,17 +776,37 @@ Findings 11). How each is kept, read and retained is this design's choice under them). - **Every other reader of which modules exist reads the catalog half, in the same milestone.** From milestone G the indexer declares no state `modules` and serves neither its tool `indexer_read_modules` nor a view of it (*Reverted*, - below), so every module that learns from it which modules exist moves in milestone G, not after: the watchman (its + above), so every module that learns from it which modules exist moves in milestone G, not after: the watchman (its manifest findings), the status view (`view_reach`), the forge module (which repositories its account writes), the holders of the seats `mesh-config`, `mesh-provisioner` and `mesh-self-check`, the claude-code module and the tool runner. Each takes a first view of the catalog half by the seat `mesh-catalog`'s verb `revision` and follows its event `catalog-revision`, through `meshcatalogclient` in Go or the seat's generated TypeScript contract in the forge module, names the seat and never the indexer, and keeps no list of its own (ADR 0656 §6, ADR 0596 §2; ADR 0615 §1, - under which every reader of the indexer's state moves in one milestone). The order is the module graph's: each - declares the verb in its `invokes`, so the served-verb edge delivers none of them to a node before an indexer serving - `revision` runs there (ADR 0615 §2, ADR 0614). The design's choice under ADR 0656 §6: one form of the fact, never - `modules` kept beside the catalog graph for these readers, which would be a second source of the same fact. How it is - checked: test 61. + under which every reader of the indexer's state moves in one milestone). A reader that misses a `catalog-revision` + (a gap in the revisions it heard) asks `revision` once more, said in its log (ADR 0615 §5). The design's choice + under ADR 0656 §6: one form of the fact, never `modules` kept beside the catalog graph for these readers, which + would be a second source of the same fact. How it is checked: test 61. + - **The forge module's tools come from mesh-sdk's TypeScript side.** mesh-sdk's TypeScript side serves the proved + caller, a verb's `callers`, a seat's publish, and a view's read and ask-again tools, so the forge module, reading + through the seat's TypeScript contract, answers `_first_views` and `_first_view_again` as the Go + parts do from `go/views`, with no follower or tool of its own. + - **The order is the module graph's, and the foundation's own rule where the graph has no edge.** Six of the + readers declare the verb in their `invokes`, so the served-verb edge delivers none of them to any node before a + build serving `revision` runs (ADR 0615 §2, ADR 0614). Two are left out of the graph's order (ADR 0614 §3, ADR + 0615 §2): the tool runner, a foundation part, and the forge module, on a cycle with the indexer, which reads the + forge through the seat `git`. For both the order is carried by ADR 0615 §4 and §6: the owner answers at once from + what it holds, and a reader started before it waits, saying so. The tool runner asks `revision` of the indexer's + own runtime, never through itself, the tool's `callers` list naming it (ADR 0615 §3, ADR 0621 §1, §2); the forge + module answers the indexer's reads through the seat `git` before it has a first view of its own, so neither waits + on the other. + - **With no view, a reader waits; it never reads "no modules".** All eight follow *A part with no view says it + waits* (below): an empty or missing view is never taken as a mesh with no modules, each says `view-waits` after + five minutes, and once it has a first view it keeps working on what it heard while the indexer is down (ADR 0615 + §6). + - **A candidate's revision moves no reader.** `catalog-revision` says whether it carries a trunk revision or a + candidate's, and a candidate's changes nothing in any of these readers; the forge module grants no write before + its first view and none from a candidate's revision (ADR 0656 §6: a running part acts only on a trunk revision's + view). - **The trunk's heads come from the catalog half.** The checker takes the trunk heads a check merges onto from the catalog half, which carries the heads, and never reads the gate's state `source` (ADR 0596 §2); with the hold and the turns removed it reads nothing of the gate at all (*Nothing merges into a repository under check*). @@ -984,7 +1009,7 @@ on another node's subject is refused by the bus. | 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) | +| 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), `assigner_puts` (each put with its step, since when, the `running` that ended it, and whether it stands: past its 30 minutes or older than the trunk's newest build) | `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` | @@ -2054,16 +2079,28 @@ later change of a kept data's form, not as a case of this milestone. ADR 0539 §9). - **A put without hold ends itself once the node runs it.** A put the assigner's tool `assigner_put` writes with `hold` false ends when the node's have runs its fingerprint healthy, and the assigner's rules write the want again - from their next pass (ADR 0546 §5); a held put stays until `assigner_release` (ADR 0546 §6). The assigner hears that - the node runs it as the node-engine seat's `running..` naming the put's fingerprint, through a durable - consumer of its own declared in its `consumes`, the one its composing waits hear too (*Composing waits per node*), - so a put asked while the node was away ends once the node says it runs it; it never reads the node's have across - the seam (ADR 0596 §2). On that event it marks the put ended, says the step as its event `put`, and from then on its - rules own the want; a `running` naming another fingerprint ends nothing. The design's choice under ADR 0546 §5: the - event the node already says is the one fact that ends the put, so a put never pins a want by being forgotten, as - lead-47's puts of a node-engine build did for seven hours on 2026-11-09 (issue 1966), and milestone G's one logged - put of its own indexer ends by itself. Built first in the issue 1966 recovery, kept whole in milestone G. How it is - checked: test 62. + from their next pass (ADR 0546 §5); a held put stays until `assigner_release`, which also ends a put without hold + (ADR 0546 §6). The design's choice under ADR 0546 §5: the fact the node already says ends the put, so a put never + pins a want by being forgotten, as a session's puts of a node-engine build did for seven hours on 2026-11-09 (issue + 1966), and milestone G's one logged put of its own indexer ends by itself. + - **Until milestone G, from the have; from G, from `running`.** The issue 1966 recovery ends the put by the read of + the node's have the assigner already makes, and keeps that end until G. In G the end moves to the node-engine + seat's `running..` naming the put's fingerprint, which milestone G gives that event, heard through a + durable consumer of the assigner's own declared in its `consumes`, the one its composing waits hear too + (*Composing waits per node*); from then it never reads the node's have across the seam (ADR 0596 §2). The order is + an edge of the module graph: the node-engine that says `running` with its fingerprint before the assigner that + ends a put on it. + - **Ended at once when the node already runs it.** At the put's ask, and at the assigner's start for every put not + ended (one written before the upgrade among them), a put ends at once when the last `running` the assigner holds + for that node and module names its fingerprint; a put of the running build pins nothing. A `running` naming + another fingerprint ends nothing. + - **The end is its own step.** The put's end is the step `ended`, written on its key and said as the assigner's + event `put`, carrying the fingerprint and the `running` that ended it (or the release that did). + - **A put that stands is said.** A put not ended 30 minutes after its want was written, or one older than the + trunk's newest build of its module, is said by `assigner_puts` and as the finding `put-stands`, naming the node, + the module and the fingerprint, which the condition keeper raises (ADR 0564 §2); it clears when the put ends. + Thirty minutes is many times a module's start window and the canary's wait, so a put moving normally raises + nothing. How it is checked: test 62. - **The bus module is wanted like any module; only a warranted want replaces its server** (ADR 0594, changing ADR 0539 §14 and §15, ADR 0546 §8 and ADR 0565 §7 for the bus module). The assigner writes the bus module's want by the same rules as every module's, and the node agent applies what that want brings (its files, its processes' bus @@ -2672,13 +2709,23 @@ What milestone G's prepare found, and its test: forge module's writable repositories and the reads of the holders of `mesh-config`, `mesh-provisioner` and `mesh-self-check`, of the claude-code module and of the tool runner each answer from the catalog half: after a merge that adds a module, each names it once `catalog-revision` is said, with no call of the indexer by name; the - indexer's manifest declares no `modules` (each module's existing tests, a row each; live, a search of the catalog's - and mesh-tools' code and manifests finds no `indexer_read_modules` and no view `modules`). + indexer's manifest declares no `modules`; a reader that misses a `catalog-revision` asks `revision` again and then + names the module (in `meshcatalogclient`'s test and the forge module's); a candidate's `catalog-revision` changes + nothing in any reader, and the forge module grants no write before its first view nor from a candidate; the tool + runner started before the indexer says it waits, takes no empty view as no modules, raises `view-waits` after five + minutes, and once it has its first view keeps answering while the indexer is stopped (each module's existing tests, + a row each; whole, tool runner; live, a search of the catalog's and mesh-tools' code and manifests finds no + `indexer_read_modules` and no view `modules`, and after milestone G's first merge that adds a module the status + view's `view_reach` and the watchman's manifest findings name it, as the operator sees them). 62. **A put without hold ends itself.** A put with `hold` false of a fingerprint the node does not run yet ends on that - node's `running` naming its fingerprint, says the step `put`, and the next pass of the rules writes the want, so a - later build is wanted with no `assigner_release`; a `running` of another fingerprint ends nothing; a held put stays - until released; a put whose `running` was said while the assigner was stopped ends after its restart (whole, - assigner; live, `assigner_puts` after milestone G's put of its own indexer). + node's `running` naming its fingerprint, at the step `ended` with the fingerprint and that `running`, and the next + pass of the rules writes the want, so a later build is wanted with no `assigner_release`; a `running` of another + fingerprint ends nothing; a put of the build the node already runs ends at its ask; a put written before the + assigner's start, whose node already runs it, ends at that start; a put whose `running` was said while the + assigner was stopped ends after its restart; a put not ended within 30 minutes, and one older than the trunk's + newest build, is listed as standing by `assigner_puts` and raises `put-stands`, which clears when it ends; + `assigner_release` ends a put without hold; a held put stays until released (whole, assigner; live, + `assigner_puts` after milestone G's put of its own indexer). ## What retires -- 2.54.0 From 76b317ed3ac94571ad519c3dee90fd1bc1b80a87 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 20:06:40 +0100 Subject: [PATCH 6/8] Take in round 3's second pass on design 47's G amendment: put-stands timed by the watchman, the put ends by done A new step would read as a running put to a reverted assigner, the running-before-assigner order is no edge of the module graph, put-stands needs a writer that survives a stopped assigner, and the tool runner's revision rests on ADR 0663, now required before the push beside ADR 0661. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../47-delivery-from-commit-to-delivered.md | 78 ++++++++++++------- 1 file changed, 48 insertions(+), 30 deletions(-) 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 295414cd..5378124c 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 @@ -4,6 +4,7 @@ status: in-progress code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-catalog modules/gitea, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, mesh-contracts, hq 00-META/checks] updated: 2026-11-09 decisions: + - "ADR 0663" - "ADR 0661" - "ADR 0660" - "ADR 0659" @@ -786,17 +787,20 @@ Findings 11). How each is kept, read and retained is this design's choice under (a gap in the revisions it heard) asks `revision` once more, said in its log (ADR 0615 §5). The design's choice under ADR 0656 §6: one form of the fact, never `modules` kept beside the catalog graph for these readers, which would be a second source of the same fact. How it is checked: test 61. - - **The forge module's tools come from mesh-sdk's TypeScript side.** mesh-sdk's TypeScript side serves the proved - caller, a verb's `callers`, a seat's publish, and a view's read and ask-again tools, so the forge module, reading - through the seat's TypeScript contract, answers `_first_views` and `_first_view_again` as the Go - parts do from `go/views`, with no follower or tool of its own. + - **The forge module's tools come from mesh-sdk's TypeScript side.** mesh-sdk's TypeScript side has no view + today; milestone G adds there a view follower with its read and ask-again tools, beside the proved caller, a + verb's `callers` and a seat's publish, so the forge module, reading through the seat's TypeScript contract, + answers `_first_views` and `_first_view_again` as the Go parts do from `go/views`, with no + follower or tool of its own. Its cost: a release of the TypeScript side rebuilds every TypeScript module, which + the delivery carries like any change of a package its users build in. - **The order is the module graph's, and the foundation's own rule where the graph has no edge.** Six of the readers declare the verb in their `invokes`, so the served-verb edge delivers none of them to any node before a build serving `revision` runs (ADR 0615 §2, ADR 0614). Two are left out of the graph's order (ADR 0614 §3, ADR 0615 §2): the tool runner, a foundation part, and the forge module, on a cycle with the indexer, which reads the forge through the seat `git`. For both the order is carried by ADR 0615 §4 and §6: the owner answers at once from what it holds, and a reader started before it waits, saying so. The tool runner asks `revision` of the indexer's - own runtime, never through itself, the tool's `callers` list naming it (ADR 0615 §3, ADR 0621 §1, §2); the forge + own runtime, never through itself, the tool's `callers` list naming it (ADR 0663, which changes ADR 0621 §1, §2 and + ADR 0615 §3; proposed, to be accepted before G's push); the forge module answers the indexer's reads through the seat `git` before it has a first view of its own, so neither waits on the other. - **With no view, a reader waits; it never reads "no modules".** All eight follow *A part with no view says it @@ -982,7 +986,7 @@ say another runs a build (ADR 0363 §2); the gate's events are granted to the ga | 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` | +| a catalog graph revision kept, saying whether it is a trunk revision or a candidate's | `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-delivery-contracts` (ADR 0641 §2) | | 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 | @@ -1009,7 +1013,7 @@ on another node's subject is refused by the bus. | 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), `assigner_puts` (each put with its step, since when, the `running` that ended it, and whether it stands: past its 30 minutes or older than the trunk's newest build) | `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) | +| 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), `assigner_puts` (each put with its step, since when, held or not, the `running` that ended it, and for a put without hold whether it is past its 30-minute bound; a put older than the trunk's newest build of its module said so) | `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` | @@ -1330,7 +1334,9 @@ the gate no longer watches the checker's verdicts for a hold. **Its record is to 0548 §2 to §4, §9 and §10, ADR 0551 and ADR 0534 §4's judgement of `mesh/check` by the merge are in force, and ADR 0661, which ends the first two and changes the third (proposed, needs the operator's Approve), is to be accepted before milestone G's pull requests are pushed; until it is accepted, the rule below holds as those records have it. ADR 0662 -is withdrawn; G builds ADR 0661. How it is checked: in the gate's whole-module test an approved merge into a +is withdrawn; G builds ADR 0661. Beside it, ADR 0663 (proposed: the indexer answers `revision` in its own runtime to +exactly the parts that invoke it, the tool runner among them) is to be accepted before the push too, since the tool +runner's first view of the catalog half rests on it (*Every other reader of which modules exist*). How it is checked: in the gate's whole-module test an approved merge into a repository under a running check is handed at once, and the gate's manifest declares no `turns` and no setting `check-gates-merge`; in the checker's, a check claims at once with no gate state granted to read. @@ -2084,23 +2090,31 @@ later change of a kept data's form, not as a case of this milestone. pins a want by being forgotten, as a session's puts of a node-engine build did for seven hours on 2026-11-09 (issue 1966), and milestone G's one logged put of its own indexer ends by itself. - **Until milestone G, from the have; from G, from `running`.** The issue 1966 recovery ends the put by the read of - the node's have the assigner already makes, and keeps that end until G. In G the end moves to the node-engine - seat's `running..` naming the put's fingerprint, which milestone G gives that event, heard through a - durable consumer of the assigner's own declared in its `consumes`, the one its composing waits hear too - (*Composing waits per node*); from then it never reads the node's have across the seam (ADR 0596 §2). The order is - an edge of the module graph: the node-engine that says `running` with its fingerprint before the assigner that - ends a put on it. + the node's have the assigner already makes, and keeps that end until G. That read crosses the seam against ADR + 0596 §2, kept on purpose until G, because no `running` event carries a fingerprint before G. In G the end moves + to the node-engine seat's `running..` naming the put's fingerprint, which milestone G gives that + event, heard through a durable consumer of the assigner's own declared in its `consumes`, the one its composing + waits hear too (*Composing waits per node*); from then it never reads the node's have. A `running` without a + fingerprint, as a node-engine from before G says it, ends no put: a put on a node whose node-engine is from before + G ends on the first `running` with a fingerprint from that node's new node-engine, which the composing waits move + first on that node, and `put-stands` says it meanwhile. - **Ended at once when the node already runs it.** At the put's ask, and at the assigner's start for every put not ended (one written before the upgrade among them), a put ends at once when the last `running` the assigner holds for that node and module names its fingerprint; a put of the running build pins nothing. A `running` naming another fingerprint ends nothing. - - **The end is its own step.** The put's end is the step `ended`, written on its key and said as the assigner's - event `put`, carrying the fingerprint and the `running` that ended it (or the release that did). - - **A put that stands is said.** A put not ended 30 minutes after its want was written, or one older than the - trunk's newest build of its module, is said by `assigner_puts` and as the finding `put-stands`, naming the node, - the module and the fingerprint, which the condition keeper raises (ADR 0564 §2); it clears when the put ends. - Thirty minutes is many times a module's start window and the canary's wait, so a put moving normally raises - nothing. How it is checked: test 62. + - **The end is a step the put already has.** The put ends with its existing step `done`, or `released` when a + release ends it, written on its key and said as the assigner's event `put`, carrying the fingerprint and the + `running` that ended it. No new step is added, so a reverted assigner, which knows `done`, `released` and + `refused` as ended, never reads an ended put as running. + - **A put without hold that stands is said, by the watchman.** When a put without hold has its want written, the + assigner says `wait-held` on the seat `mesh-wants` for it (node, module, fingerprint, since when) with a bound of + 30 minutes, and `wait-ended` when it ends. The watchman times it as it times every wait, and past the bound writes + the finding `put-stands`, naming the node, the module and the fingerprint, which the condition keeper raises + (ADR 0564 §2) and clears on `wait-ended`; at its own start the watchman seeds its open puts from `assigner_puts`, + as it seeds `acting-part-late`, so a put whose assigner stopped after writing its want is still timed. Thirty + minutes is many times a module's start window and the canary's wait, so a put moving normally raises nothing. A + held put is listed by `assigner_puts` as held, with since when, and raises nothing: holding is what it was asked + for. `assigner_puts` also lists a put older than the trunk's newest build of its module. How it is checked: test 62. - **The bus module is wanted like any module; only a warranted want replaces its server** (ADR 0594, changing ADR 0539 §14 and §15, ADR 0546 §8 and ADR 0565 §7 for the bus module). The assigner writes the bus module's want by the same rules as every module's, and the node agent applies what that want brings (its files, its processes' bus @@ -2713,18 +2727,22 @@ What milestone G's prepare found, and its test: names the module (in `meshcatalogclient`'s test and the forge module's); a candidate's `catalog-revision` changes nothing in any reader, and the forge module grants no write before its first view nor from a candidate; the tool runner started before the indexer says it waits, takes no empty view as no modules, raises `view-waits` after five - minutes, and once it has its first view keeps answering while the indexer is stopped (each module's existing tests, - a row each; whole, tool runner; live, a search of the catalog's and mesh-tools' code and manifests finds no + minutes, and once it has its first view keeps answering while the indexer is stopped; the forge module answers + `_first_views` with the revision it holds and the one it waits for (each module's existing tests, a row + each; whole, tool runner and forge module; live, a search of the catalog's and mesh-tools' code and manifests finds no `indexer_read_modules` and no view `modules`, and after milestone G's first merge that adds a module the status view's `view_reach` and the watchman's manifest findings name it, as the operator sees them). 62. **A put without hold ends itself.** A put with `hold` false of a fingerprint the node does not run yet ends on that - node's `running` naming its fingerprint, at the step `ended` with the fingerprint and that `running`, and the next - pass of the rules writes the want, so a later build is wanted with no `assigner_release`; a `running` of another - fingerprint ends nothing; a put of the build the node already runs ends at its ask; a put written before the + node's `running` naming its fingerprint, at the step `done` carrying the fingerprint and that `running`, and the + next pass of the rules writes the want, so a later build is wanted with no `assigner_release`; a `running` of + another fingerprint ends nothing; a `running` without a fingerprint ends no put, and the node's new node-engine's + `running` with one ends it; a put of the build the node already runs ends at its ask; a put written before the assigner's start, whose node already runs it, ends at that start; a put whose `running` was said while the - assigner was stopped ends after its restart; a put not ended within 30 minutes, and one older than the trunk's - newest build, is listed as standing by `assigner_puts` and raises `put-stands`, which clears when it ends; - `assigner_release` ends a put without hold; a held put stays until released (whole, assigner; live, + assigner was stopped ends after its restart; a reverted assigner reads the ended put as ended; a put without hold + not ended 30 minutes after its want says `wait-held` and the watchman raises `put-stands`, which clears on its + `wait-ended`, and with the assigner stopped after the want is written `put-stands` is still raised; a held put + past 30 minutes raises nothing and is listed by `assigner_puts` as held with since when; `assigner_release` ends a + put without hold at the step `released`; a held put stays until released (whole, assigner and watchman; live, `assigner_puts` after milestone G's put of its own indexer). ## What retires @@ -3016,7 +3034,7 @@ and what answers it: | 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 | +| 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; a put that never ends | `wait-held` at the start with its bound, timed by the watchman; held and failed outcomes and `record-ended`; `view-waits` after five minutes; `put-stands`, timed by the watchman from the put's `wait-held` | | 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 | -- 2.54.0 From b3484cdcfc50186be5d5845925a27fe9c065d554 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 20:07:35 +0100 Subject: [PATCH 7/8] Name the indexer's mesh-wide hold among what milestone G retires and a revert restores The per-node wait is the assigner's, so the indexer's hold states and tools go with its modules state. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- 03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5378124c..e170de4c 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 @@ -710,7 +710,7 @@ Findings 11). How each is kept, read and retained is this design's choice under | 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, nor its tool `indexer_read_modules` or its view, every reader of it reading the catalog half (*Every other reader of which modules exist*, under *The revision view*, below) | yes: `modules` as it stood at G's delivery, and the consumer's position | + | 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`; the states `holds`, `engine-reads` and `hold-bounds` of its mesh-wide hold | catalog graph revisions as objects, form 1; `modules` no longer declared, nor its tool `indexer_read_modules` or its view; the mesh-wide hold, its three states and its tools `indexer_holds` and `indexer_engine_reads` gone, the wait per node being the assigner's (*Composing waits per node, by the assigner*); every reader of it reading the catalog half (*Every other reader of which modules exist*, under *The revision view*, below) | yes: `modules` and the hold's three states as they 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 | -- 2.54.0 From c9abb1c5f92e2216b8a980cf487510d42736bcb7 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 9 Nov 2026 20:12:00 +0100 Subject: [PATCH 8/8] Test 58 names a part not heard, not a node away: a check still passes a sleeping node with a warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test read as failing every check while a node sleeps, against ADR 0593 §9 and the design's own try section. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../01-to-be/47-delivery-from-commit-to-delivered.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 e170de4c..2c371b94 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 @@ -2703,12 +2703,12 @@ What milestone G's prepare found, and its test: 57. **The indexer reads every definition at its start.** Started with a definition on a contracts trunk that no event announced, the indexer judges the first manifest claiming that seat with it, and answers it in `seat-definitions` (whole, indexer). -58. **A check that could not run says so and is asked once more.** A check whose composer's node does not answer - reads `could not run` and names that node in `mesh-delivery.show`; the claim is released; the node's `running` asks - the check again once, and a second `running` of the same part at the same fingerprint asks no second check; a +58. **A check that could not run says so and is asked once more.** A check that a part of the mesh it needs does not + answer (the composer, the forge, a build seat's holder) reads `could not run` and names that part in + `mesh-delivery.show`; the claim is released; the part's `running` asks the check again once, and a second `running` of the same part at the same fingerprint asks no second check; a `could not run` not asked again within 60 minutes raises `check-could-not-run`, which reads cleared once the heads - are checked again, replaced, or their pull request closes (whole, checker; live, read through - `mesh-delivery.show`). + are checked again, replaced, or their pull request closes; a placed node away is not such a part: the check passes it + with a warning (ADR 0593 §9) (whole, checker; live, read through `mesh-delivery.show`). 59. **A session hears an accepted record** (issue 595). An Approve that accepts a record makes the knowledge base say `reviewed` with what was accepted, by the operator and with the warrant, and the tool runner's account hears it, so a session waiting with `mesh_wait` wakes on it (whole, knowledge base; live). -- 2.54.0