From 23ef14d0ba6b3d6a49134db1621cade36999f5e4 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 13:24:48 +0200 Subject: [PATCH 01/10] Give ten uncited decisions their homes in the designs they change (issue 504) The knowledge base's cited rule failed for ADR 0303, 0306-0312, 0314 and 0315: accepted, and nothing cited them. Each design that owes the decision now names it in its decisions and says it in prose, so the records can name these designs as their homes. --- 03-DESIGN/01-to-be/06-the-controller.md | 26 +++++++++---- .../27-a-module-requires-the-mesh-resolves.md | 36 +++++++++++++++++- .../01-to-be/32-what-a-module-declares.md | 14 ++++++- ...-the-shell-and-the-accounts-environment.md | 34 ++++++++++++++++- .../42-the-machines-modules-in-order.md | 17 ++++++++- .../45-a-core-that-cannot-fail-silently.md | 20 +++++++++- .../46-the-conversation-with-the-operator.md | 37 ++++++++++++++++++- .../47-delivery-from-commit-to-delivered.md | 29 ++++++++++++++- .../51-the-registries-keep-what-is-named.md | 35 +++++++++++++++++- 9 files changed, 230 insertions(+), 18 deletions(-) diff --git a/03-DESIGN/01-to-be/06-the-controller.md b/03-DESIGN/01-to-be/06-the-controller.md index d2e59aa4..cbaaa56a 100644 --- a/03-DESIGN/01-to-be/06-the-controller.md +++ b/03-DESIGN/01-to-be/06-the-controller.md @@ -3,8 +3,9 @@ layer: to-be status: in-progress code: - mesh-controller -updated: 2026-10-10 +updated: 2026-10-12 decisions: + - "ADR 0315" - "ADR 0001" - "ADR 0005" - "ADR 0006" @@ -64,11 +65,20 @@ applications** — first-party, shipped with everything else, and running on the anything else does. A task does not need to know a node exists, and *being ours does not make something infrastructure*. `ai` is folded into `config`: a provider licence is an ordinary grant. -**`record` is an open question rather than an eighth entry.** Contexts integrate through it -(ADR 0008), which makes it load-bearing, -and *where it lives* is unresolved, tracked by issue 414 since -research 006 closed — putting it in the foundation risks recreating the circularity the tier design just -removed. Listing it here would settle by naming what has not been settled by arguing. +**`record` is not an eighth entry: it is the bus** (2026-10-12, ADR 0315). Contexts integrate through +it (ADR 0008), and where it lives was left open by research 006, for fear that putting it in the foundation +would recreate the circularity the tier design removed. ADR 0315 writes down what later records built: the +record is the bus's, the EVENTS stream, where each module says the events it declares (ADR 0041), and the +key-value buckets each module declares for its current state, which another module names in `reads` to read +(ADR 0201). Asking another context is a call to its verbs over the bus, request and reply, and is not the +record; a secret, and whatever a context keeps out of its buckets, is asked for. The controller's own records +are read from its record mirrors (ADR 0289) or asked of its verbs, never from its store. The record lives in +the foundation's bus, not in the store, which stays each context's private database reached only with that +context's own credential, so no store holds a record shared between contexts and the circularity does not +return. No module owns the record whole: each event and each bucket belongs to the module that declares it, +which alone writes it. *How it is checked:* where each rule was decided — the runtime's refusal of a bucket +not issued or a write to one only read (ADR 0201), one credential per context's store (ADR 0008), and the +mirrors' events counted live and compared with the store by probe D17 (ADR 0289). **One of the seven is built.** `inventory` owns a database of that name and holds the node records; the rest do not exist. What it takes to run any of them — the language, and what must already be @@ -225,8 +235,8 @@ every public name. - ~~**The contexts themselves.**~~ **Decided** — seven, by ADR 0006. - What remains open is narrower and named there: **where the record lives**, which research 006 - leaves unresolved because the foundation is the one place it must not go. + What remained open, **where the record lives**, is decided by ADR 0315: on the bus, in the EVENTS + stream and the buckets each module declares, never in a store (above). - **How far it may be split.** One deployable today. Splitting a context out costs the single interface a surface depends on (research 011). diff --git a/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md b/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md index 7fe64154..05453177 100644 --- a/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md +++ b/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md @@ -2,8 +2,10 @@ layer: to-be status: in-progress code: [mesh-controller internal/catalogue] -updated: 2026-10-08 +updated: 2026-10-12 decisions: + - "ADR 0312" + - "ADR 0309" - "ADR 0262" - "ADR 0225" - "ADR 0112" @@ -151,6 +153,24 @@ ADR 0107). never created, owned or removed by the mesh. The module requires read or read-write access. Where the data is, is an operator value on the assignment. +*A read-write access writes through the path's own group (2026-10-12, ADR 0312, extending ADR 0051; the +build is tracked by issue 537):* when a module declares a `read-write` access, the node-engine reads the group +that owns the path as it applies, and makes the identity the module's code runs as a member of it: a +container's process gains it as a supplementary group, and an account that runs the module's tools is put in it +as ADR 0252 puts an account in a group. The module then writes the path as the path's group may, and as nothing +else. The path itself is never changed: no owner, group or mode is set on it or under it, and a read-write +access whose path's group cannot write is stated unhealthy, saying that the operator decides. A `read` access +grants no group. A path owned by group 0, or by a group that would let the identity act as root, is refused by +name. A group the node-engine granted is taken back when no declared read-write access asks for it any more, +never one the identity held before (ADR 0252 §3). A container is recreated with the group, and a service of +the machine's own service manager that runs the module's tools as an account is restarted once the account +holds it; a unit of the account's own service manager, or a login session, waits for a new login as ADR 0252 +§5 and ADR 0254 say. The tool runner's account so gains the groups of every read-write access of the modules it +serves: the boundary is the account's, not the module's. *How it is checked:* the node-engine's tests ADR 0312 +lists (the group given and the path's owner, group and mode unchanged; a read access granting nothing; an +unwritable group unhealthy and group 0 refused; the group taken back; the container recreated and the system +unit restarted holding the group). + **A port** is what ADR 0038 already decided: the module says which port its software uses, and the node-engine answers with where the machine put it. @@ -212,6 +232,20 @@ gains a setting by shipping its current value as the default, and `settings` lis source: the default, the mesh, or the node. The first module to use it is the notifier, whose font size and width are preferences. *How it is checked:* the table in ADR 0262. +*A setting left unset reads a value the modules share (2026-10-12, ADR 0309, changing ADR 0164 §7 and §8 and +ADR 0262 §3, §7, §8 and §10; the build is tracked by issue 415):* every module keeps declaring each setting it +takes, so any one module can still be set apart, and a setting may also name a mesh-wide value it shares with +other modules, such as `desktop.font-size`; the desktop modules are the first to declare the shared names they +read. When none of a module's own layers sets one of its settings, the setting reads the mesh-wide value of its +shared name, and only when that is unset too the module's own default. Read from the bottom, last winning: the +module's default, the shared value, the module's mesh-wide layer, the node's. `settings` sets and clears a +shared value without naming a module, and still refuses a write without a module that names no declared shared +name. Both the listing of one module and the listing of preferences across all modules name the shared name as +a value's source, beside the default, the mesh and the node. Setting a shared value is held to the strictest +rule among the settings that read it, so a trusted setting (ADR 0277) is never gone round through its shared +name. Whether a shared value is a number, a colour or a string is left to the typed-settings work, issues 346 +and 410. *How it is checked:* the table in ADR 0309. + *The operator's value travels only where it is asked for (2026-09-30, [issue 173](../../04-ISSUES/173-a-modules-settings-reach-every-fact-it-contributes/00-report.md)):* a setting overrides a key a contribution or a served fact declares and adds none; a file keeps taking diff --git a/03-DESIGN/01-to-be/32-what-a-module-declares.md b/03-DESIGN/01-to-be/32-what-a-module-declares.md index de6cb6a1..691f146e 100644 --- a/03-DESIGN/01-to-be/32-what-a-module-declares.md +++ b/03-DESIGN/01-to-be/32-what-a-module-declares.md @@ -12,8 +12,9 @@ code: - mesh-tools src/main.ts - mesh-catalog modules/mesh-catalog - mesh-tools node-tools/internal/runtime (a module's state, ADR 0201) -updated: 2026-10-07 +updated: 2026-10-12 decisions: + - "ADR 0307" - "ADR 0240" - "ADR 0233" - "ADR 0201" @@ -260,6 +261,17 @@ them on the bundle's channel — refusing, with the reason, a name it was not is bucket it only reads. A watch hands the current values first, then every change: a bundle that starts late, or starts again, has the whole of the state before it has any of the news. +*A key's kept values are read too (2026-10-12, ADR 0307, extending ADR 0201).* A bucket keeps as many past +values per key as its owner declared, and the runtime serves them: `history`, given a state and a key, answers +every value the bucket still keeps for that key, oldest first, each with its revision, whether it was a put or +a delete, and when it was written; an empty answer for a key it holds nothing for. It is a read like `get`: +served to the owner and to every reader, refused with the reason to a module the bucket was not issued to, and +it writes nothing. The Go SDK's `State(name)` carries it as `History(key)`, beside `Get`. Its first user is the +issue tracker's switch to its log (ADR 0300 §6), which carries every value its bucket still keeps; that work is +tracked by issue 502. *How it is checked:* the runtime's tests (several values and a delete answered oldest +first, with revision, operation and time; an empty answer for an unknown key; a reader served, a module the +bucket was not issued to refused, nothing written) and the SDK's tests of `History`. + The owner says how many past values a key keeps and how long a value lives, as a seat says how long its backlog survives (§3); the mesh caps a value's size and a bucket's. **A bucket outlives its module's assignment** — what a module stored is data diff --git a/03-DESIGN/01-to-be/41-the-shell-and-the-accounts-environment.md b/03-DESIGN/01-to-be/41-the-shell-and-the-accounts-environment.md index 287efc33..2a5c8705 100644 --- a/03-DESIGN/01-to-be/41-the-shell-and-the-accounts-environment.md +++ b/03-DESIGN/01-to-be/41-the-shell-and-the-accounts-environment.md @@ -2,8 +2,9 @@ layer: to-be status: in-progress code: [mesh-host, mesh-controller, mesh-catalog] -updated: 2026-10-09 +updated: 2026-10-12 decisions: + - "ADR 0310" - "ADR 0268" - "ADR 0252" - "ADR 0203" @@ -88,6 +89,7 @@ WP4 the service manager's module, finished (mesh-catalog) independ WP5 assign and prove (operator-gated) needs WP1–WP3 merged and rolled WP6 a module puts the account in a group (mesh-host, mesh-controller, mesh-catalog) issue 247 WP7 the control-node withholds execute (mesh-controller, mesh-catalog) ADR 0268 +WP8 a node's files and accounts are read (mesh-sdk, mesh-catalog, mesh-controller) ADR 0310, issue 418 ``` WP1, WP2 and WP4 are independent, and are built in parallel on one feature branch per repository @@ -334,3 +336,33 @@ out, the control-node's value is set to `withhold` and zsh is pushed to it. Then 2. the control-node's `zsh_config` answers `execute` as not served, and says why; 3. `mesh_search` for `node-login-shell execute` names the other three nodes and not the control-node; 4. a call to the control-node's `node-login-shell.execute` is refused, and the other nodes still answer. + +## WP8 — A node's files and accounts are read through `node-reads` + +*`mesh-sdk`, `mesh-catalog`, `mesh-controller`. ADR 0310, extending ADR 0268; tracked by issue 418.* + +ADR 0268 §5 has narrow read tools take the common reads over from `execute`, so that an approval is asked for an +act and not for a read, and ADR 0286 §44 sets the approval mark on `execute` only once those reads have a tool on +every node. Two had none: reading or searching a file, and looking up an account or a group. + +- **A node seat, `node-reads`, on every node**, the control-node included, held by the tool runner; the + controller holds the seat's definition beside the other node seats. +- **Three reads:** a file read bounded in size, a search by fixed string bounded in matches and in depth, and a + lookup of an account or a group. Each verb says what it replaces (ADR 0245): `cat`, `grep`, `getent passwd`, + `getent group` and `id`. The bounds are set by the build and stated in each verb's description. +- **It reads as an account of its own**, which the mesh creates on every node, in no group that holds secrets, + with no login shell and no home of the operator's, so the node's file modes bound what it sees. `/proc` and + `/sys` are read as far as that account may. +- **Secret places are refused by name** before any read: the vault's directories, every module's own-secret + directory and the known credential files, whatever the file modes would allow. +- **One content rule.** The rule the operator channel applies to what it sends moves into `mesh-sdk`, and both + the operator channel and every `node-reads` answer apply it. + +Once the seat answers on every node, the approval mark on `node-login-shell.execute` is set as ADR 0286 §44 says, +under the same issue. + +**Proof.** The rows of ADR 0310's *how it is checked*: the controller's seat listing names a holder of +`node-reads` on every node; the tool runner's tests of the bounds, the account and the refusal by name; one +`mesh-sdk` test of the content rule and one in each of its users; `mesh_search` for `cat`, `grep` and +`getent passwd` finding the verbs. Live, the kernel's RAID status file is read through the seat on the home +server, and the agent's credentials file on the laptop is refused. diff --git a/03-DESIGN/01-to-be/42-the-machines-modules-in-order.md b/03-DESIGN/01-to-be/42-the-machines-modules-in-order.md index a021773a..174fe209 100644 --- a/03-DESIGN/01-to-be/42-the-machines-modules-in-order.md +++ b/03-DESIGN/01-to-be/42-the-machines-modules-in-order.md @@ -2,8 +2,9 @@ layer: to-be status: in-progress code: [mesh-catalog, mesh-controller, mesh-host] -updated: 2026-10-10 +updated: 2026-10-12 decisions: + - "ADR 0303" - "ADR 0215" - "ADR 0173" - "ADR 0040" @@ -120,6 +121,20 @@ not write into its include directory. Each contribution is a dependency on the s so assigning one of them without a window manager is refused. The first versions, which still write the include files themselves, move to contributions once the controller derives the dependency. +**A package may be declared by several modules** (2026-10-11, ADR 0303, changing ADR 0210 §1, §10 and §11 +for packages). Two modules on one node that each need a program may each declare its package, and it is +present while any of them declares it. Declaring a package asserts only that it is present: the node-engine +installs one the machine lacks, never upgrades it, and never removes it because a declaration went away, so a +second declaration changes nothing on the machine and taking one module off leaves the package to the others. +A module that needs a program another seat holder installs may declare that package itself, or still depend on +that seat. One module declaring a package present while another declares it absent (ADR 0180) on one node is +refused at assignment and at composition, naming both. A tool's configuration files and the contributions to +its seat keep their one owner, as above: only the package itself may be declared by several modules. *How it +is checked:* the controller's tests (two declarations of one package are assigned; present against absent is +refused, naming both modules) and the node-engine's tests ADR 0303 requires (a package declared twice is +installed once, one no module declares any more is not removed, an installed one is reported unchanged and not +upgraded). + **Added 2026-10-04.** `triggerhappy` holds `node-hotkeys` on both workstations (ADR 0212). The laptop model's vendor keys become its contribution. The window-manager fragments of the launcher, the clipboard, the wallpaper, the bar and the laptop model become `config` contributions to diff --git a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md index 8fb7183f..e762ae39 100644 --- a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md +++ b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md @@ -2,8 +2,9 @@ layer: to-be status: in-progress code: [mesh-controller, mesh-host, mesh-tools, mesh-catalog, mesh-sdk, mesh-lab] -updated: 2026-10-11 +updated: 2026-10-12 decisions: + - "ADR 0314" - "ADR 0293" - "ADR 0289" - "ADR 0285" @@ -531,6 +532,22 @@ the controller's replay R301 and its tests of the rule). > its cause, so the exception is read from the verbs. Nothing decided changes: ADR 0230's acts stay > exempt, and pushes by hand — what roll-out by default (ADR 0236) exists to end — still count. +**A condition no healer can fix becomes an issue** (2026-10-12, ADR 0314, extending ADR 0285; the build is +tracked by issue 409). A condition no healer can fix is one whose kind no healer answers, one a healer passes +over because it does not apply, or one handed to the operator after a healer's budget was spent. When such a +condition is raised, passed over or handed on, and no issue links its key (ADR 0285), the issue tracker opens +one itself, from the controller's condition events, with the condition's summary, its evidence and its key, and +links the key at once; the controller sets no link. Every message about the condition then names the issue +through its tracking line. Agent sessions take such an issue as any other: a claiming move that names the +worker, a branch, review and the required checks; no standing agent acts on a condition by itself. What an +agent does about it is written in the issue's notes, and every act it makes by hand on the mesh is also written +to the hand-act log, which S15 reads. ADR 0275 §9's week stands: the tracker opens no issue for a condition +before 2026-10-17 20:17 UTC, and until then such a condition is linked by whoever opens an issue for it. *How +it is checked:* the tracker's tests feeding condition events (one issue opened and linked for each of the three +cases, none for a condition a healer acts on or one already linked, none before the week ends); live, the +messenger's preview of such a condition says "Tracked as issue N", and the issue's notes and the hand-act log +hold what the agent did. + ## 8. Staged core upgrades and rollback (rule 8) **Health, per core component**, as probes in the registry (§4): @@ -881,4 +898,3 @@ in [to-be 47](47-delivery-from-commit-to-delivered.md), and their progress is tr - Routing by presence, quiet hours, answering back through a channel, and an external dead-man service — decided by ADR 0234, designed in [to-be 46](46-the-conversation-with-the-operator.md). -- A condition that needs judgement handed to an agent as work — tracked by issue 409, since research 017 closed. diff --git a/03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md b/03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md index 05b9f72b..da34cd72 100644 --- a/03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md +++ b/03-DESIGN/01-to-be/46-the-conversation-with-the-operator.md @@ -2,8 +2,9 @@ layer: to-be status: in-progress code: [mesh-controller, mesh-tools, mesh-sdk, mesh-catalog] -updated: 2026-10-10 +updated: 2026-10-12 decisions: + - "ADR 0308" - "ADR 0286" - "ADR 0277" - "ADR 0259" @@ -289,6 +290,40 @@ Presence facts are events on the bus. The router keeps them as **current state o machine and per intake kind, overwritten — never as a history, and never in a message's words. A consumer that wants a timeline of the operator's day is a decision of its own. +### While a delivery walks + +*2026-10-12, ADR 0308; the build is tracked by issue 532.* + +A delivery restarts what it moves, and those restarts raise conditions that clear once it is done; a router +that restarted used to say again what it had been told to keep quiet (issue 531). So the router holds what a +walk touches and tells the walk's result once. + +- **A walk names what it touches.** The controller's walk events say, when a walk starts and again when it + ends with its outcome (delivered, failed or stopped), the modules it moves and the machines it sends them to. + What it touches is those modules on those machines, and every module a moved tool runner or node-engine + carries there. +- **The router holds what an open walk touches.** A message whose subject is a touched module or machine is + held instead of sent, kept with when it was held, and `history` says it was held. A message several open + walks touch is held until the last of them ends or its bound passes. +- **Some messages are never held:** an ask (§6), a secret given or replaced, a message about anything no open + walk touches, and a walk's own failure. +- **A walk's end is told once:** one message with its outcome, the modules and machines it reached and how + long it took, then each held message whose condition is still open, one line each. One whose condition + cleared during the walk is only counted. A walk delivered with nothing held and nothing open is one line, or + nothing on a channel set to say only what needs the operator. +- **A hold has a bound:** twice the largest delivery budget in force among the walk's deliveries + ([to-be 47](47-delivery-from-commit-to-delivered.md)): 20 minutes for a core module, 10 for leaf modules at + a five-minute budget and 6 at a three-minute one; 15 minutes, not doubled, for a walk none of whose + deliveries has a budget. Past it, every held message still open is sent with a line saying the walk still + runs and since when, and the cleared ones are counted. A walk that hangs never silences the mesh. +- **A restart says nothing it has said:** a router that restarts says again only what is open and was never + answered, never a silenced message or one withheld for its content. + +*How it is checked:* the controller's tests of the walk events, and the router's tests of holding, never +holding, the end message, the bound and the restart, as ADR 0308 lists them; live, the next delivery of the +tool runner gives the operator one message at its end, and the channel's `history` shows what was held and +released. + ### The content rule The router's, applied before anything reaches any holder; §9 says what it allows. A `private` holder 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 3625ebea..378763d9 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 @@ -2,8 +2,9 @@ layer: to-be status: in-progress code: [mesh-controller, mesh-catalog modules/mesh-delivery] -updated: 2026-10-11 +updated: 2026-10-12 decisions: + - "ADR 0306" - "ADR 0299" - "ADR 0298" - "ADR 0282" @@ -232,6 +233,32 @@ shown and held to no budget. Measured on 51 walks on 2026-10-09 and 10, a leaf w - **Every phase is kept**: the window, the wait, the build, the send, each reading, the rest's first healthy report; the plan shows an expected landing time from the last seven days' medians. +*Walks beside each other (2026-10-12, ADR 0306, extending ADR 0304, which narrowed the bullets above to what +a change shares).* Building the walks at once found what those bullets left open, and ADR 0306 decides it: + +- **A walk's builds are its own, never another's backlog.** A send composes a module whose build another open + walk owns at the build its node was last sent, and neither carries nor judges it: that build is moved and + judged only by its own walk, so a failed walk puts back only its own builds. What waits on a node with no open + walk to move it is still backlog, carried and judged by the next gated send there (this changes ADR 0236 §17 + for builds an open walk owns). +- **A delivery group still assembles.** A merge whose delivery group has a member not yet merged waits for the + merge window whether or not a batch is assembling, so a group's members walk in one batch, in their order. +- **Related means everything the walks would move.** A module in common is any module a walk plans to build or + send, the dependents of what its merges moved included; a change whose modules are not known yet is related to + every change. Two related walks are neither cut beside each other nor judged beside each other on one node. +- **A starved judgement is said.** A leaf judgement set aside by core builds three times in a row raises a + warning naming the walk and the core walks that set it aside. +- **A node's own fault fails every judgement on it.** A node found unwell as a whole (a failed apply, an + unhealthy network or engine) fails every judgement open on it, leaf and core, each walk putting back only its + own builds. +- **A rollback beside another walk's judgement** behaves as it does today; whether it should wait is tracked by + issue 528. + +*How it is checked:* the controller's tests ADR 0306 lists, one per clause: an unsent build of walk A left out +of walk B's send and not taken up as backlog while A is open; a group's second member joining its first's batch; +overlapping dependents keeping two walks apart; the warning on the third set-aside; a failed apply failing every +open judgement on its node. + **Who builds what.** | Part | Owner | What it adds | diff --git a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md index ca2e748f..06dad5ed 100644 --- a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md +++ b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md @@ -6,8 +6,9 @@ code: - mesh-catalog modules/artifact-store-tools (the store's tools), modules/distribution (its unbuilt code removed) - mesh-catalog modules/gitea (package retention) - mesh-catalog modules/docker (image pruning) -updated: 2026-10-08 +updated: 2026-10-12 decisions: + - "ADR 0311" - "ADR 0251" - "ADR 0189" - "ADR 0257" @@ -110,7 +111,8 @@ ADR 0257: failed one, and the controller keeps a row per copy in `artifact_mirrored`. The copies the existing build records already named were recorded by the migration that made the table. - **Kept by the kept builds that stood on it** (`stood-on`), and by nothing else: a definition never - keeps a copy. Otherwise a copy is eligible, like any artifact the mesh made. + keeps a copy. Otherwise a copy is eligible, like any artifact the mesh made. *(A copy of a pinned + third-party image is the exception, below: ADR 0311 §3.)* - **Held whole.** A copy counts as held only when its index and every platform the index names are held; otherwise it is copied again. A copy a build copies again is no longer recorded as let go of. - **An index goes with its platforms only in a confirmed collect.** Before its first delete, it reads @@ -122,6 +124,35 @@ ADR 0257: `unrecorded_mirrors`, with what it alone marks. The controller's `mirrors` verb records the ones a person names, and the sweep then decides about them. +### Pinned third-party images + +*2026-10-12, ADR 0311, extending ADR 0257; the build is tracked by issue 421.* A third-party image a module +pins was held only upstream, and an image withdrawn there was found only when a node that lacked it tried to +deploy (issue 113). So the mesh holds a copy of what its modules pin, and checks both sources: + +- **Every pinned third-party image is copied** into the artifact store when it is first used, into the + repository of its upstream image (`upstream//`, as above). +- **Its record names the pin.** The copy's row names, as its reason, the module whose definition pinned the + digest (changing ADR 0257 §6, which named only a build or a person's reason). +- **Kept while a module pins it.** A copy is kept, with the reason `pinned`, while any module's current + definition pins its digest; neither the sweep after a build nor the nightly clean-up lets it go. For these + copies a definition does keep a copy, and one is eligible only when no kept build stood on it and no current + definition pins it (changing ADR 0257 §8, §9 and §20). `artifact_store_references` shows them with the reason + `pinned`. +- **A node pulls from the mesh first**, from the artifact store's copy, and from the upstream only second. +- **A scheduled check pulls every pinned image as an empty node would**, once from the copy and once from the + upstream. An upstream that no longer serves while the copy does raises a warning condition naming the module + and the image; neither serving raises an urgent one; each clears once the image is served again. An upstream + that serves while the copy does not is not decided yet (issue 421); a node then still pulls from the upstream. +- **A module may declare its upstream**, the object store's module first, with its maintained fork, and a view + shows each declared upstream's last release and open advisories, so ADR 0288's triggers of no release for six + months and of an advisory unpatched for 30 days can be read there. + +*How it is checked:* the table in ADR 0311 — the controller's copy and its row, the collection test of keeping +by pin, the node-engine's pull from the store with the upstream unreachable, the check's two conditions, the +manifest test of a declared upstream, the view against a recorded answer, and live, the object store's pinned +image held in the store. + ### The controller's verbs for the store - **`artifacts`** reads only. It answers every reference the mesh recorded making, with its state and, From fe4f62c712dd5098259b40f71729f14c6e14171d Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 13:28:12 +0200 Subject: [PATCH 02/10] to-be 55: a failed whole-set rule is a warning to the operator and leaves the module healthy, and cited is judged in the review (issue 504) The operator decided on 2026-10-12, on issue 504, that a rule of the record set that fails is a fault of the records, not the module failing: health says only whether the module works (issue 508's principle), so a finding must not hold a walk at the module's first-node gate. --- 03-DESIGN/01-to-be/55-the-knowledge-base.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/03-DESIGN/01-to-be/55-the-knowledge-base.md b/03-DESIGN/01-to-be/55-the-knowledge-base.md index 677b82d1..4243c378 100644 --- a/03-DESIGN/01-to-be/55-the-knowledge-base.md +++ b/03-DESIGN/01-to-be/55-the-knowledge-base.md @@ -2,7 +2,7 @@ layer: to-be status: designed code: [] -updated: 2026-10-11 +updated: 2026-10-12 decisions: - "ADR 0290" --- @@ -125,8 +125,17 @@ approval. On `approve`: On acceptance the record and its clauses move to `accepted` and `in force`, every clause its `changes` and `supersedes` name moves to `changed` or `superseded`, and `accepted` and `changed` events are emitted. Then the 4 -rules that judge the whole record set run (an accepted decision cited by nothing, and the like); a failure raises a -condition naming the rule and the records, and does not undo the acceptance. +rules that judge the whole record set run (an accepted decision cited by nothing, and the like). A failure does not +undo the acceptance. It is a fault of the records, which their authors repair, not of the module: it is told to the +operator once, as a warning naming the rule and the records, and cleared once the rule holds again. The module's +health names it and stays healthy, because health says only whether the module works, and a finding never holds a +walk at the module's first-node gate (the operator's decision of 2026-10-12 on issue 504, as issue 508 decided for a proposal never +reviewed). + +The rule `cited` is also judged ahead, in the review, before the judgement: a decision that names no home and that no +accepted record links is not accepted. Its review, by the knowledge base itself, asks changes naming the rule (issue +504). It is not refused at `propose`, because no record can link a number only reserved: a record that cites a new +decision is proposed after it and accepted, and the new decision is proposed again. An accepted record is changed only by a new record, or by a progressive insight: a new version marked as one, which is reviewed like a proposal. @@ -184,7 +193,8 @@ repository keeps no copy of the records, generated or otherwise (issue 435, answ |---|---| | A proposal that breaks a per-record rule is refused, naming every reason | the module's table test, one case per rule of § 4 | | A record is accepted only after the knowledge base's own judgement approves it, and, when it needs the operator, only on the warrant | the module's tests: a proposal whose judgement answers changes, or cannot be had, stays proposed; a `needs operator` record unaccepted without the warrant, accepted with it | -| A whole-set rule that fails is loud | the module's test: an acceptance that leaves a decision cited by nothing raises the condition | +| A decision is not accepted cited by nothing | the module's test: a decision with no home and no record linking it gets changes from the review, with no judgement asked; with a home, or once a record citing it is accepted, it is accepted | +| A whole-set rule that fails is loud, and leaves the module healthy | the module's test: a correction that leaves a decision cited by nothing tells the operator once, as a warning naming the rule and the record, not again after a restart, and clears it once a record cites it; the module's health stays healthy throughout | | Every version is kept and comparable | the module's test: three versions, `history` lists them, the difference of the first and third is exact | | The import changes nothing on a second run, and loses nothing | the module's test over a fixture repository; and the comparison of § 8 on issue 426 | | A drafted clause is never served as in force | the module's test: `holds` over a record with only drafted clauses answers none, and `ask` marks them | From 94e7e044b95a579f71fdbff044d0bcb4060f94b6 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 13:50:29 +0200 Subject: [PATCH 03/10] Say in to-be 47 that a covered stop reads covered and stays listed, and that a one-machine module is covered (issue 480) --- .../01-to-be/47-delivery-from-commit-to-delivered.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 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 378763d9..dabf67d8 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 @@ -103,7 +103,11 @@ delivery merged with no walk answering it within ten minutes is held too, with t forge says that later merge commit contains it (`gitea_contains`, asked once per pair and kept). Merge order is only the first filter, and a forge that cannot answer means not yet covered. `mesh/delivery` says `covered: a later merge that contains it was delivered` as a success. A module that runs on one - machine only gets no "rest" step and is never covered; + machine only is covered too: its walk records the empty rest as sent once its first machine passes + (ADR 0270, as corrected 2026-10-12); +- *a stop whose walk ended without building its modules* (issue 474) stays `stopped`, a final state, and + is read as covered once a later merge covers it by the same test: `mesh/delivery` then says `covered after + its stop` with the covering merge, once, as a success. No state or transition is added (issue 480); - the covered row is tried before `go`, from `held` and from `ready` past the ten minutes. **History is retired, not held** (ADR 0250). @@ -334,7 +338,7 @@ In this order, each by its owner: | Verb | Answers or does | |---|---| -| `deliveries` | every delivery not final, and the final ones of the last day, one line each; filtered by state, repository or group | +| `deliveries` | every delivery not final, the final ones of the last day, and every final one `stalled` still lists, one line each; filtered by state, repository or group | | `show` | one delivery or group whole: plan, transitions, machine steps, order, the walk | | `groups` | every group with its members in order and its derived state | | `what-if` | the delivery plan a diffset would have, asked of the controller's planner, kept nowhere | From e8cff1646a9a4adda0a17720b50e968578ae1d05 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 13:50:33 +0200 Subject: [PATCH 04/10] Carry ADR 0309 and ADR 0319 in one paragraph of to-be 27, replacing the 0309 paragraph alone (issue 415) --- .../27-a-module-requires-the-mesh-resolves.md | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md b/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md index 05453177..8550f27f 100644 --- a/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md +++ b/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md @@ -5,6 +5,7 @@ code: [mesh-controller internal/catalogue] updated: 2026-10-12 decisions: - "ADR 0312" + - "ADR 0319" - "ADR 0309" - "ADR 0262" - "ADR 0225" @@ -232,19 +233,25 @@ gains a setting by shipping its current value as the default, and `settings` lis source: the default, the mesh, or the node. The first module to use it is the notifier, whose font size and width are preferences. *How it is checked:* the table in ADR 0262. -*A setting left unset reads a value the modules share (2026-10-12, ADR 0309, changing ADR 0164 §7 and §8 and -ADR 0262 §3, §7, §8 and §10; the build is tracked by issue 415):* every module keeps declaring each setting it -takes, so any one module can still be set apart, and a setting may also name a mesh-wide value it shares with -other modules, such as `desktop.font-size`; the desktop modules are the first to declare the shared names they -read. When none of a module's own layers sets one of its settings, the setting reads the mesh-wide value of its -shared name, and only when that is unset too the module's own default. Read from the bottom, last winning: the -module's default, the shared value, the module's mesh-wide layer, the node's. `settings` sets and clears a -shared value without naming a module, and still refuses a write without a module that names no declared shared -name. Both the listing of one module and the listing of preferences across all modules name the shared name as -a value's source, beside the default, the mesh and the node. Setting a shared value is held to the strictest -rule among the settings that read it, so a trusted setting (ADR 0277) is never gone round through its shared -name. Whether a shared value is a number, a colour or a string is left to the typed-settings work, issues 346 -and 410. *How it is checked:* the table in ADR 0309. +*A value several modules share is set once (2026-10-12, ADR 0309, changing ADR 0164 §7 and §8 and ADR 0262 +§3, §7, §8 and §10, and ADR 0319, changing ADR 0164 §4 and §9 and ADR 0309 §2, §3, §5 and §6; the build is +tracked by issue 415):* a module keeps every setting it takes, and a preference may also name a mesh-wide +**shared name**, such as `desktop.font-size`. Unset in every layer of its own module, the preference +reads the shared value, and only when that is unset too its default; read from the bottom, last +winning: the default, the shared value, the module's mesh-wide layer, the node's. So a person sets +the interface font size once and every module that names it follows, while any one module or node +can still be set apart in its own layer. A shared value is a fourth place of change beside the three +of ADR 0164 (a new default no longer reaches an assignment whose shared value is set), and the plan +of a change to it names every assignment, of every module that reads it, +whose effective value moves. It is set and cleared through the `settings` verb without a module, and +both `settings` listings name the shared name as a value's source beside the default, the mesh and the +node. **A shared value reaches no trusted setting** (ADR 0319): a trusted setting may not declare a +shared name, and registration refuses one that does, so a shared value is never trusted and needs no +warrant; a trusted setting is set in its own module's layer, on the operator's warrant (ADR 0277) or at +the terminal, as before. A trusted setting that a stored manifest still declares with a shared name +never reads the shared value, and both listings say why beside it. Whether a shared value is a +number, a colour or a string waits for the typed-settings work (issues 346 and 410). *How it is +checked:* the tables in ADR 0309 and ADR 0319. *The operator's value travels only where it is asked for (2026-09-30, [issue 173](../../04-ISSUES/173-a-modules-settings-reach-every-fact-it-contributes/00-report.md)):* From 12e59b3c422fb417300685ce80a139fa68805520 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 14:15:03 +0200 Subject: [PATCH 05/10] to-be 55: a drafted clause may be changed, and a clause another record links keeps its number (issue 494, ADR 0321) Names ADR 0321 in its home, so the knowledge base's cited rule holds through the design that carries it. --- 03-DESIGN/01-to-be/55-the-knowledge-base.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/03-DESIGN/01-to-be/55-the-knowledge-base.md b/03-DESIGN/01-to-be/55-the-knowledge-base.md index 4243c378..924dbcb0 100644 --- a/03-DESIGN/01-to-be/55-the-knowledge-base.md +++ b/03-DESIGN/01-to-be/55-the-knowledge-base.md @@ -5,6 +5,7 @@ code: [] updated: 2026-10-12 decisions: - "ADR 0290" + - "ADR 0321" --- # 55 — The knowledge base @@ -157,6 +158,14 @@ per clause whether the record says exactly that; an accepted clause becomes `con same way. When every record of a topic has been through both, the operator is told once per topic, with the counts confirmed and still drafted. `return` with a record's number and a reason sends it back to be drafted again. +**A drafted clause may be changed, and a linked clause keeps its number** (ADR 0321, issue 494). A later record's +`changes` may name a drafted clause, confirmed or not, as it may one in force or changed; only a superseded or proposed +clause is refused. On that record's acceptance the drafted clause is marked `changed`, naming the clause that changed +it, keeps its number and stays `drafted`: it is still never served as in force, and `get` and `ask` say both. Because +a redraft numbers a record's clauses anew, `return` refuses a record any of whose clauses another record changed or +links by number, in a version served or proposed, naming each such link; a record that changes the drafted clause is +the way to correct it. A record no other record links by clause is returned as before. + ## 8. The import `import` takes a commit of this repository (and `dry-run`) and writes every decision record and research effort @@ -198,6 +207,7 @@ repository keeps no copy of the records, generated or otherwise (issue 435, answ | Every version is kept and comparable | the module's test: three versions, `history` lists them, the difference of the first and third is exact | | The import changes nothing on a second run, and loses nothing | the module's test over a fixture repository; and the comparison of § 8 on issue 426 | | A drafted clause is never served as in force | the module's test: `holds` over a record with only drafted clauses answers none, and `ask` marks them | +| A drafted clause may be changed, and a linked clause is never renumbered | the module's tests: a record changing a drafted clause is accepted, and the clause is then `changed`, keeps its number, stays drafted and is not served; `return` of a record whose clause another record changed or links by number is refused naming the link, and writes nothing | | An answer stays within its bound | the module's test over the full import of `main` at a recorded commit: every `ask`, `get` and `holds` of a fixed question list | | A record written in this repository meanwhile writes its clauses | `records.py`, check `clauses`, on every hq pull request, until the records left this repository (issue 426); since then `records.py`, check `no-records`, refuses any record written here | | Live | the acceptance sentence, asked from a session outside this repository | From f63e60a4a16b485c59fd85e0cfc81458b6210c5d Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 15:29:38 +0200 Subject: [PATCH 06/10] Give ADR 0313 a home in design 47, the delivery budget it extends The knowledge base's whole-set cited rule (to-be 55, ADR 0313's own review) refuses to accept a record nothing cites. Design 47 already builds on ADR 0282's delivery budget, which ADR 0313 extends with the operator's wider fix-time target, so it is the record's home. --- .../01-to-be/47-delivery-from-commit-to-delivered.md | 9 +++++++++ 1 file changed, 9 insertions(+) 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 dabf67d8..c741eda0 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] updated: 2026-10-12 decisions: + - "ADR 0313" - "ADR 0306" - "ADR 0299" - "ADR 0298" @@ -222,6 +223,14 @@ shown and held to no budget. Measured on 51 walks on 2026-10-09 and 10, a leaf w (7 of 20 within five), a core walk of one tier 5.6 (worst 8.8), and a controller walk dragging two catalogue tiers 14.2 (issue 338). +*A wider measure, around the delivery budget (2026-10-12, ADR 0313, extending ADR 0282).* The delivery budget +above runs from the merge; the operator's own target is wider, the **fix time**, which runs from an issue's +first move to `fixing` to the moment its last fix is delivered. Each issue's class carries a **fix budget** too +(five minutes leaf, ten core, the delivery budget's own figures), and the tracker splits a fix time into four +phases — implementing, review, checks, and merge to running (the delivery time above) — so a fix over its +budget is said loud, naming its longest phase, and clears at the next fix of its class delivered within budget. +The delivery budget stays as written: a measure inside the fix budget, not replaced by it. + **What changes in the walk.** - **A lone merge is cut at once**: no batch assembling, and no member of its delivery group still unmerged. From e373474db74dd640c5b64d17e08fa3053cedd02e Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 17:29:59 +0200 Subject: [PATCH 07/10] =?UTF-8?q?Give=20ADR=200323=20its=20place=20in=20to?= =?UTF-8?q?-be=2047,=20to-be=2045=20=C2=A79=20and=20the=20glossary=20(issu?= =?UTF-8?q?e=20341)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A merge check now judges the head merged into a named trunk commit and its verdict goes out of date when the trunk moves; the designs and the glossary say so, with the tests that hold it. --- 00-META/glossary.md | 8 ++++-- .../45-a-core-that-cannot-fail-silently.md | 15 ++++++++++- .../47-delivery-from-commit-to-delivered.md | 26 +++++++++++++++---- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/00-META/glossary.md b/00-META/glossary.md index 04bd1286..cf622668 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -305,13 +305,17 @@ and the operator act. **Recorded by** the controller's `identity` and `licences` **Purpose.** To take one commit from its pull request to every node that should run it, and to put it back when it fails. **Recorded by** the `mesh-delivery` module (deliveries) and the controller (the planner, the walk). **Downstream of** Module, Core, Health and repair and Data. -**Decided by** ADR 0162, 0218, 0236, 0237, 0238, 0239, 0250, 0267, 0270, 0276. +**Decided by** ADR 0162, 0218, 0236, 0237, 0238, 0239, 0250, 0267, 0270, 0276, 0323. **Uses:** module, node, declaration and send (Placement), health (Health and repair), person. - **merge check** — the two statuses a pull request carries before it may merge: the **merge gate** (`mesh/merge-gate`, the build seat's composed check of the modules the change touches) and the **repository check** (`mesh/repo-check`, the repository's own `merge-check.sh`) - (ADR 0238). + (ADR 0238). Both judge the merge of the pull request's head into a named **trunk commit**, made in the + build seat's own clone and never pushed (ADR 0323). +- **trunk commit** — the commit of the trunk a merge check's verdict was judged against: the trunk's head when + the build seat cloned it. The verdict holds for that commit only, and goes out of date when the trunk moves + (ADR 0323). - **build seat** — the seat whose holder builds modules, `node-build-agent`; its holder on a node is the **builder**. Any node may hold the seat, so there is no build node by nature (ADR 0237). diff --git a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md index e762ae39..87d005b5 100644 --- a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md +++ b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md @@ -4,6 +4,7 @@ status: in-progress code: [mesh-controller, mesh-host, mesh-tools, mesh-catalog, mesh-sdk, mesh-lab] updated: 2026-10-12 decisions: + - "ADR 0323" - "ADR 0314" - "ADR 0293" - "ADR 0289" @@ -671,7 +672,19 @@ R310). A repository with none has "no repository check defined", a success where the branch does not require `mesh/repo-check` and a failure for a person where it does. **A note is never a warning on a required status**: the forge combines `warning` as a failure, so a note — a wide rebuild, a problem already so on the base — is a success that says it, and only -what a person must decide fails (issue 293; checked by the forge module's status tests). The +what a person must decide fails (issue 293; checked by the forge module's status tests). +Revision, ADR 0323 (2026-10-12). **The gate judges what merging makes, not the head.** The build seat takes +the trunk the pull request merges into, its head at that moment as the check's **trunk commit**, and makes in +its own clone the merge of the head into it, as the forge's default merge would; both layers judge that +merge, and a head that does not merge cleanly fails, naming the files in conflict. The merge is only checked: +never pushed, never registered. The verdict names the head and the trunk commit in both statuses' words +("head … merged into trunk …"), in mesh-delivery's `checks` and `show` and in the kept delivery plan, and +names the commits of the other repositories the check read; their move is issue 568's question. When the +trunk moves, the forge's holder puts both statuses of every open pull request into it back to pending with +the reason and runs nothing; the merge tool refuses a verdict whose trunk commit is not the trunk's head, +asks that head's check once, says how long the last check of the repository took, and never merges by +itself (checked by the builder's `TestReplay341`, replayed as R341, and the forge module's `trunk` and +`guard` tests). The check's result is the commit's **delivery plan** — the build plan, the deploy plan machine by machine with what is not an ordinary send, and the verdict — posted on the pull request. The plan is one object with a state machine, kept, followed by the release and written to the commit as a note; which parts are built 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 c741eda0..0b2ece77 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] updated: 2026-10-12 decisions: + - "ADR 0323" - "ADR 0313" - "ADR 0306" - "ADR 0299" @@ -81,7 +82,7 @@ allows. | State | Means | Bound | H2 after the bound | |---|---|---|---| -| proposed | a head was announced and is being checked | 1 hour | nothing: the check's own watchdog (S6) speaks | +| 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 | nothing: the check's own watchdog (S6) speaks | | checked | a verdict arrived | 1 minute | nothing: it is decided at once | | ready | it passed; it waits to be merged | none | — | | rejected | it failed its check | none | — | @@ -90,7 +91,16 @@ allows. | delivering | its walk runs; its builds are asked and registered tier by tier | 2 hours | `delivered`, `failed` or `superseded`, as the walk's record says | | delivered, failed, superseded, stopped, retired, covered | final | — | — | -The transitions are those of ADR 0239 decision 2 and ADR 0270. A delivery that is +**A verdict holds for one trunk commit** (ADR 0323). The check judges the head merged into the trunk's head +at the moment the build seat clones it, the **trunk commit**, and the verdict names both. When another merge +moves the trunk, a `ready` delivery goes back to `proposed` on the observed event `trunk moved`, with both +commits in the transition's reason; a `rejected` one stays `rejected`, its statuses saying the verdict is out +of date. Nothing is asked for it: a recheck, a new head or the merge tool asks the check again. The row from +`proposed` to `checked` takes only a verdict that names the trunk's head as its trunk commit, so a verdict for +an older trunk is kept and shown and does not count, and the rows reach a fixed point. Checked by +mesh-delivery's `TestATrunkMoveSendsAReadyDeliveryBackToProposed` and the table's own walk. + +The transitions are those of ADR 0239 decision 2, ADR 0270 and ADR 0323. A delivery that is merged while not `ready` goes to `held`. Its check did not pass, and only a person decides that it goes on. A delivery merged with no walk answering it within ten minutes is held too, with the true reason, and listed by `stalled` at once. Nothing waits silently. @@ -308,9 +318,15 @@ only once it is built. first, then *built by* and *version skew*, then *engine before controller*. The pair shows the rule that won and the rules it won over. Rules of one rank both ways are a contradiction. The group is `rejected`, and the reason names both members, both rules and the `after:` line that would settle it. -- **Check**: the members' heads composed together, one gate over every machine with all their definitions. It - is asked by mesh-delivery through `delivery-check` when the group forms or a member's head moves, and - posted on every member's head as `mesh/delivery-group`. +- **Check**: the members' heads composed together, one gate over every machine with all their definitions, + each head merged into its own trunk's head first and named with that trunk commit (ADR 0323). It is asked + by mesh-delivery through `delivery-check` when the group forms or a member's head moves, and posted on + every member's head as `mesh/delivery-group`. A member rebased or pushed is a new head with its own check: + it takes its old head's place in the group, even once the group is delivering, and the composed check runs + again for the new heads instead of holding the group. A merge into a member's trunk that is not one of the + group's members puts the composed check out of date: the group reads `checking` with the reason, and the + composed check runs again once a member is judged again. Checked by mesh-delivery's + `TestAGroupsComposedCheckGoesOutOfDateOnAMergeThatIsNotItsMember` and `TestARebasedMemberIsComposedAgainNotHeld`. - **Delivering**: in order. A member waits in `published` until the one before is `delivered` or `covered`. A member published while its group's composed check is asked and not answered waits there for the answer; one held because that check had not passed is *lifted* back to `published` once it passes or warns for the From 09916e2bbc076a46d721588e9caee98230050923 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 17:46:42 +0200 Subject: [PATCH 08/10] Say in to-be 47 what ADR 0323 leaves as it is, and give to-be 45's revision its own paragraph The review asked that the trunk-less verdicts, mesh-delivery's trunk head, the refuse-ask race and the lost check's recheck be said, and found the revision spliced mid-paragraph. --- .../45-a-core-that-cannot-fail-silently.md | 13 +++++++------ .../47-delivery-from-commit-to-delivered.md | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md index 87d005b5..769c2c79 100644 --- a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md +++ b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md @@ -672,7 +672,12 @@ R310). A repository with none has "no repository check defined", a success where the branch does not require `mesh/repo-check` and a failure for a person where it does. **A note is never a warning on a required status**: the forge combines `warning` as a failure, so a note — a wide rebuild, a problem already so on the base — is a success that says it, and only -what a person must decide fails (issue 293; checked by the forge module's status tests). +what a person must decide fails (issue 293; checked by the forge module's status tests). The +check's result is the commit's **delivery plan** — the build plan, the deploy plan machine by machine with +what is not an ordinary send, and the verdict — posted on the pull request. The plan is one object with +a state machine, kept, followed by the release and written to the commit as a note; which parts are built +is Phase 5's. + Revision, ADR 0323 (2026-10-12). **The gate judges what merging makes, not the head.** The build seat takes the trunk the pull request merges into, its head at that moment as the check's **trunk commit**, and makes in its own clone the merge of the head into it, as the forge's default merge would; both layers judge that @@ -684,11 +689,7 @@ trunk moves, the forge's holder puts both statuses of every open pull request in the reason and runs nothing; the merge tool refuses a verdict whose trunk commit is not the trunk's head, asks that head's check once, says how long the last check of the repository took, and never merges by itself (checked by the builder's `TestReplay341`, replayed as R341, and the forge module's `trunk` and -`guard` tests). The -check's result is the commit's **delivery plan** — the build plan, the deploy plan machine by machine with -what is not an ordinary send, and the verdict — posted on the pull request. The plan is one object with -a state machine, kept, followed by the release and written to the commit as a note; which parts are built -is Phase 5's. +`guard` tests). **The replays.** mesh-lab carries a scripted scenario for each core incident, asserting the rule's outcome, run on every merge to mesh-controller, `mesh-host` and mesh-tools: 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 0b2ece77..8e7be134 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 @@ -100,6 +100,22 @@ of date. Nothing is asked for it: a recheck, a new head or the merge tool asks t an older trunk is kept and shown and does not count, and the rows reach a fixed point. Checked by mesh-delivery's `TestATrunkMoveSendsAReadyDeliveryBackToProposed` and the table's own walk. +What this leaves as it is, said so nobody reads more into it: + +- **mesh-delivery's trunk head is the last merge it heard** into that trunk while the delivery was open. A push + to the trunk that is not a merge it hears does not move it; the forge's holder, which reads the branch, and + the merge tool, which compares commits when asked, still catch it. +- **A verdict that names no trunk commit**, from a build seat older than ADR 0323, counts in mesh-delivery as it + did: such a delivery stays `ready`. The forge reads the same verdict out of date once its trunk moves, and the + merge tool refuses it and asks the check. The two agree once the head is judged again. +- **A verdict no build judges** (a change that touches no module, in a repository with no `merge-check.sh` or + not the mesh's) names the base branch's head the forge announced with the head as its trunk commit. +- **In a busy repository the merge tool can refuse, ask, and refuse again**: another merge may move the trunk + while the asked check runs, and the new verdict is then out of date too. Each refusal asks once more; the + merge goes through once a check finishes before the next merge. +- **A check that was asked and never answers** is listed by `stalled` an hour after the ask, and is asked again + with `mesh-delivery.recheck`, which the merge tool's refusal names. + The transitions are those of ADR 0239 decision 2, ADR 0270 and ADR 0323. A delivery that is merged while not `ready` goes to `held`. Its check did not pass, and only a person decides that it goes on. A delivery merged with no walk answering it within ten minutes is held too, with the true reason, and listed by From 52c19c0628421b63e1b991e4fac6e9f67912b984 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 20:38:23 +0200 Subject: [PATCH 09/10] =?UTF-8?q?Name=20the=20take=20of=20a=20shared=20fol?= =?UTF-8?q?der=20in=20the=20core's=20writers=20table=20(ADR=200325=20?= =?UTF-8?q?=C2=A78,=20issue=20567)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The controller sends a machine the take act on the operator's warrant, on mesh.node..ask.access-act; the machine verifies the mesh's signature and records the act. Rule 1 asks every kind of state to name its one writer. --- 03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md | 1 + 1 file changed, 1 insertion(+) diff --git a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md index 769c2c79..b0a657a7 100644 --- a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md +++ b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md @@ -80,6 +80,7 @@ Every kind of state the core keeps, and the one component that writes it. Anyone | a machine's declaration | controller (lease holder) | the bus, last per subject | read | | a hand-over asked of a machine ([issue 356](../../04-ISSUES/356-the-operators-command-ran-a-stale-engine-and-a-hand-over-had-no-channel/00-report.md)) | controller, at its terminal | the machine, beside its state | the engine verifies the mesh's signature and records it, or refuses | | a fresh setuid search asked of a machine ([issue 361](../../04-ISSUES/361-the-control-nodes-agent-account-is-never-judged/00-report.md)) | controller, at its terminal | the machine, which throws its last search away | the engine verifies the mesh's signature and starts it, or refuses | +| the take of a shared folder asked of a machine (ADR 0325 §8, issue 567; subject `mesh.node..ask.access-act`) | controller, on the operator's warrant | the machine, which records the act | the engine verifies the mesh's signature and takes it, or refuses | | a machine's applied state and its report | the node-engine's apply queue (§6) | the machine; the report on the bus | the reconcile and a delivery *enqueue*, never apply | | the controller lease | the controller instance holding it | key-value `mesh-controller_lease` | a candidate waits | | plans and their tiers | controller (lease holder), compare-and-set on the plan's revision | the controller's store | read through `plans` | From 7456e24563a9c4b246ffa2ef57d477c817d7a611 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 12 Oct 2026 20:39:12 +0200 Subject: [PATCH 10/10] List ADR 0325 among the decisions to-be 45 carries --- 03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md | 1 + 1 file changed, 1 insertion(+) diff --git a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md index b0a657a7..c78d5dc9 100644 --- a/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md +++ b/03-DESIGN/01-to-be/45-a-core-that-cannot-fail-silently.md @@ -4,6 +4,7 @@ status: in-progress code: [mesh-controller, mesh-host, mesh-tools, mesh-catalog, mesh-sdk, mesh-lab] updated: 2026-10-12 decisions: + - "ADR 0325" - "ADR 0323" - "ADR 0314" - "ADR 0293"