diff --git a/00-META/glossary.md b/00-META/glossary.md index fc4eecb9..80130957 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -978,7 +978,7 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564, 0668. takes a restore point of a module's kept data on the live bus before the module moves across a `form` change, sets the live data aside and restores the point at a revert, and lists and removes restore points; the bus module and the snapshot account restore nothing into the live bus, and the backup holder only stores copies (design 47 G §554). - **aside** — live kept data a restore set aside, removed only by the operator's `discard-aside`. + **aside** — live kept data a restore set aside, removed only by the operator's hand tool `recovery_discard_aside`. *Not:* ~~restorer~~, ~~restore account~~ - **disk readings** — the size of each data item a node's modules declare, measured by its `measure` and kept with its largest size in seven days by disk-load on that node, in its own state, one key 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 784118cc..cedccf88 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 @@ -1340,7 +1340,8 @@ and `delivered` events. - **A module may ship its own Dockerfile.** A build module of its own builds the module's `image` artifact from it, rootless, with no root and no container runtime socket on the build node; a pull request's image is built and thrown away, and only a merged trunk commit's image reaches the registry, by digest. From R4g that module is the only part that builds an image, and build-agent builds none (ADR 0487 §7, ADR 0524 §5); the other builds are Go and Node programs, which their build modules build. - **Every dependency comes by the version its lock file names**, checked against its checksum: the mesh's own - code from the mesh's forge, its own packages from its package registry, any other from its public source. + code from the mesh's forge, its own packages from its package registry, any other from its public source; a mesh + package's entry is the build's to write, by the version the planner read (ADR 0573 §2; design 47 G §571). - **A build module fetches from the forge over SSH with a key of its own**, as its own account, under a forge account for that module that reads the mesh's repositories and pushes nothing. - **A build's reuse key** holds the installed toolchain's version and the build module's recipe version in place diff --git a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md index 5671a93a..e10436d5 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md +++ b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md @@ -88,7 +88,8 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l verb `branch` (its head, and whether a rule refuses a direct push and admits only merges), and refuse a watched branch the forge does not protect, naming it (ADR 0666 §3). - §6 **The foundation parts are the manifest validator's list.** The manifest validator's list (ADR 0619 §3) holds - exactly the foundation parts, ADR 0619 §1's and the runner, which joins them (ADR 0666 §4); the checker leaves the + exactly the foundation parts, ADR 0619 §1's, the runner, which joins them (ADR 0666 §4), and the forge and image + registry modules (§584); the checker leaves the list when it is removed (*How milestone G itself is checked and delivered*). A registration holds no foundation value. How it is checked: a unit test of the manifest validator refuses a template in the manifest of every part on its list, the runner's included, and judges every other part's manifest as any part's. @@ -366,9 +367,9 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l they are a chain (`mesh-grants:program`, then `node-tools:runtime`, then `mesh-grants:tools`). - §48 **Removal runs the other way.** When something goes away the order is reversed: a seat moved to a new holder has its old holder released before the new one claims (ADR 0666 §14). -- §49 **The edges alone order.** The build and the delivery order by these edges alone; a cycle of delivery edges (kinds 2, - 4, 5, 6) is delivered as one step, and a cycle of build edges (kinds 1 and 3) is refused, naming every artifact on it - (ADR 0666 §14; research 086, R17). The cycle rule is the indexer's, in the catalog graph it keeps, so the checking +- §49 **The edges alone order.** (§569) The build and the delivery order by these edges alone; a cycle of delivery edges + (kinds 2, 4, 5, 6) alone is delivered as one step, and a cycle with any build edge (kind 1 or 3), whatever its other + edges, is refused, naming every artifact on it (ADR 0666 §14; research 086, R17). The cycle rule is the indexer's, in the catalog graph it keeps, so the checking program's cycle rule goes with the checker. - §50 **A format's reader first.** A format a running reader cannot read is preceded by its reader, an edge of kind 4 on every watched branch (ADR 0666 §14): the indexer's manifest validator for a new field of a seat definition or a diff --git a/03-DESIGN/01-to-be/47-milestones/G-12-develop-answers-fourth.md b/03-DESIGN/01-to-be/47-milestones/G-12-develop-answers-fourth.md index 25a6d6e5..bf0b2a1e 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-12-develop-answers-fourth.md +++ b/03-DESIGN/01-to-be/47-milestones/G-12-develop-answers-fourth.md @@ -5,6 +5,8 @@ design: 47 milestone: G updated: 2026-11-11 decisions: + - "ADR 0500" + - "ADR 0573" - "ADR 0672" - "ADR 0667" - "ADR 0666" @@ -64,8 +66,8 @@ that round are written into the lines they name, here. Questions that an earlier `node-backup` on its node as every item is (ADR 0404), which stores the copy and nothing else. When `restore-kept` answers `missing`, the node agent calls it again with `from-backup: yes`: the recovery module asks the seat `node-backup`'s `restore` of its item for that directory, which brings it back beside its path as that - verb always does, takes the point from there and restores it; when no repository snapshot holds it, `restored` - says `failed` with `restore point missing`, and §300 holds the revert. + verb always does, takes the point from there and restores it once its digest is checked (§567); when no + repository snapshot holds it, `restored` says `failed` with `restore point missing`, and §300 holds the revert. - **The aside, and a restore that fails.** `restore-kept` sets the live kept data aside by taking a stream snapshot of each of its bus objects into `aside///` in the same directory, reads the aside back to check it (`at` the UTC time written `20261111T121200Z`, with `-2`, `-3` and on added when that name exists), only then removes the live objects, which needs no person's warrant, the data staying in the aside (the operator, @@ -99,7 +101,7 @@ that round are written into the lines they name, here. Questions that an earlier (recovery module), `kept-points` lists a point and an aside with their sizes, an aside is gone after `recovery_discard_aside`, and a restart lists the same. - §523 **A seat event whose subject has parts.** A seat definition's `subject` with `` tokens (§397), such as - `mesh-recovery`'s `ended..` and `node-backup`'s `item-started..` (§554), is said on + `mesh-recovery`'s `ended..` and `node-backup`'s `item-started...` (§554), is said on `mesh.seat..event.` followed by the subject, each part filled in the definition's order: the seat generator's subject function for the event takes each part as a string in that order and fills each as one token, escaped as a key's name is (§73), refusing an empty part, `.` and `..`. A reader hears one module's events by the subject with @@ -179,9 +181,13 @@ that round are written into the lines they name, here. Questions that an earlier with no `parts` (from an assigner of before G, or for a node whose record does not state `want-parts`, §525) is applied as today, its resources fetched by their sources and checked by their own digests. A want with `parts` is applied only when the digest of every resource it fetched is among the values of the want's `parts`, and - every value of `parts` is the digest of a resource it fetched; otherwise it applies nothing of that module and its - have says the phase `parts-differ`, naming the digest. `parts` stays keyed by `:` for people and - the delivery record. This completes §379. Why: the digest is what is fetched and checked (develop question H-8). How + every value of `parts` is the digest of a resource it fetched; otherwise it applies nothing of that module, its + have says the phase `held` (one of the seat's phases, which gains none) with its detail opening `parts-differ:` and + naming each digest that differs, and it says `wait-held` with the reason `parts-differ` (§541). `parts` + stays keyed by `:` for people and + the delivery record. This completes §379. Why: the digest is what is fetched and checked (develop question H-8); a + hold is the phase `held` with its reason, as every hold of §300, §421 and §499 is, so the seat's phases stay as they + are (develop question of 2026-11-11 on H-8's phase, batch 5). How it is checked: unit (node-engine), a want whose parts and fetched digests agree applies, and one with a fetched digest outside `parts` or a part not fetched applies nothing, naming it; whole (node-engine of G), a want written by an assigner of before G is applied. @@ -355,9 +361,17 @@ that round are written into the lines they name, here. Questions that an earlier - §528 **The checker built from G's heads asks no group build.** From G's delivery the pipeline is the check and the run on request the try (§66), so the checker built from G's heads makes no group check: its group build through tagged versions (its check's group path, `judge.go`'s, and `tagwanted.go`) goes with `tag-wanted` (§373), nothing replaces it in - the checker, and a group's builds against its package members' digests are the runner's (§373, §465). Until G's + the checker, and a group's builds against its package members' digests are the runner's (§373, §465). The + checker's old Node form goes with it: it no longer packs a group member's package for a Node fetch (ADR 0500 §5), + for any group, since a group's builds against its members' heads are the runner's (ADR 0573 §4). The checker's + manifest of G's heads consumes neither package registry's `published` and its asks stream carries no `tag-wanted`; + its `go.mod` requires nothing that no code of it imports; and the checker's seat definition in mesh-contracts + defines no `tag-wanted` holder event, which goes as §321 retires `tag-wanted`. Until G's delivery the running checker makes G's own check with main's code (ADR 0667 §2). Why: §66 and §373 (develop - question CK10). How it is checked: the code review finds no group build in the checker of G's heads. + questions CK10 and the checker developer's three after it, batch 5). How it is checked: the code review finds no + group build and no Node packing in the checker of G's heads; unit (manifest validator), the checker's manifest of + G's heads accepted without the two `consumes`; unit (mesh-contracts), the checker's seat definition holds no + `tag-wanted`. - §534 **The wait's `step` in the runner's body.** The runner's wait body keeps `step` as the pipeline step a wait inside a pipeline is in (`lint`, `unit`, `integration`, `composition`, `review`, `approval`), and gains the value `pipeline`, the acting step of §203, for a wait of the entries the runner holds for its new version (§266, diff --git a/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md b/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md index 927cab97..c8fba9cf 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md +++ b/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md @@ -5,6 +5,7 @@ design: 47 milestone: G updated: 2026-11-11 decisions: + - "ADR 0259" - "ADR 0672" - "ADR 0436" - "ADR 0667" @@ -51,25 +52,22 @@ are written into the lines of G-12 they name.* - **Its seat, module and node.** The seat `mesh-recovery`, held once for the mesh, is defined in mesh-contracts beside `mesh-broker`, and the module `mesh-recovery`, in mesh-catalog, claims it; its glossary word is the *recovery module*. It runs on the node of the holder of `mesh-broker`, read from the catalog half and the seat - `mesh-module-assigner`'s placements, never a fixed name: G's seed writes its placement there, and the - placement verb refuses to move it while `kept-points` lists a point younger than 90 days, naming them, so its - points and their copies in that node's repository stay together. Its data directory, its manifest's `data` item - `points`, is capped at 10 GiB and measured by the disk readings (ADR 0571 §5): past 80 % it writes the finding - `points-filling`, and a `snapshot-kept` that would pass the cap ends `failed`, naming it, which holds the move as a - failed backup does (§296). Its `findings` state and finding stream are declared as §529's other parts declare + `mesh-module-assigner`'s placements, never a fixed name: G's seed writes its placement there. It moves as any + module that holds data, its points and asides holding no move (§562). Its data directory, its manifest's `data` + item `points`, is capped by its setting `points-cap` and measured by the disk readings (ADR 0571 §5): past its fill + mark it writes the finding `points-filling` (§561), and an act that would pass the cap ends `failed`, naming it, + before it removes anything; a `snapshot-kept` that ends so holds the move as a failed backup does (§296, §564). Its `findings` state and finding stream are declared as §529's other parts declare them, so it waits on the bus module for them and is no foundation part (ADR 0619 §1). - **Its verbs answer at once; the result is the event.** `snapshot-kept` and `restore-kept` answer `accepted` with the act's `act` id, `busy` with the act that runs (it runs one act at a time and keeps no queue), or `error`, and nothing more; the point comes on `ended`, the restore on `restored`. A caller told `busy` asks again when it hears - that act's event. The module writes each accepted act in its directory before it answers; at its start an act + the ending event of the act it was named (§560). The module writes each accepted act in its directory before it answers; at its start an act accepted and not ended is ended `failed` with `stopped`, so its caller asks again. - - **Its events.** On `mesh.seat.mesh-recovery.event.` and kept last per subject, 90 days (§396): `ended..` - and `restored..`, each carrying `act`, `module`, `kept`, `form`, `fingerprint`, `snapshot` and - `outcome` (`done` or `failed`, with `error`), `ended` also the point whole; `restore-started..` when a - restore begins; `set-aside..` and `aside-discarded..` with the aside's `id` and - `size`; and `point-removed..` (§551). The node agent wakes only on the event whose `form` and - `fingerprint` are its ask's. Its wait for `ended` or `restored` is bounded at 90 minutes (§405), past which its have - says the wait and the watchman's `delivery-waits` raises it. + - **Its events.** On `mesh.seat.mesh-recovery.event.` and kept last per subject, 90 days (§396): `ended`, + `restore-started`, `restored`, `set-aside`, `aside-discarded` and `point-removed`, each with the parts + `.`, and `expiry-ended`, their fields as §560 gives them. The node agent wakes only on the ending + event of its own act, and its wait for `ended` or `restored` is bounded at 90 minutes from its first ask (§405, + §560), past which its have says the wait and the watchman's `delivery-waits` raises it. - **Who calls it.** `snapshot-kept` and `restore-kept` are called by a node-engine holder (`seat:node-engine`): the recovery module takes a call only from an account the bus proves to be a node-engine's, and the identity of who asked is the check (the operator, 13:06 CET), every act logged; no want is read for it, since the operator, @@ -85,16 +83,24 @@ are written into the lines of G-12 they name.* - **The operator's hand tools.** `recovery_take` (take a point of one module and kept data now), `recovery_restore` (restore one point) and `recovery_retry` (ask again the last failed act of a module and kept data), each of class `act`, callers `operator` and `sessions` (§391), written in the hand-act log; they are checked by identity, as above. - - **Its account.** Composed by the grants part from the seat's definition and the manifest: the bus's stream + §566 adds `recovery_restore_aside` and `recovery_cancel`. + - **Its account.** The operator, 2026-11-11 about 14:15 CET at lead-47's terminal (the knowledge base's review of + ADR 0672, R3): the recovery module is one process with a bus account of its own (ADR 0436 §1, as ADR 0626 §1 has + the bus module's keeper), which holds the seat `mesh-recovery` and answers its verbs itself; each verb is one its + caller names (`by-caller`, ADR 0259 §3), so the bus proves who calls: a node-engine's account for `snapshot-kept` + and `restore-kept`, and the tool runner's account, which the sessions and the operator reach the bus as, for its + hand acts. Its tools bundle, carried by the tool runner, calls those verbs and holds no grant of its own. ADR 0672 + §4 records that this changes ADR 0436 §3 and §5 for this process alone. The account is composed by the grants part + from the seat's definition and the process's `bus` declaration: the bus's stream snapshot and restore subjects with their chunk subjects, a stream's deletion and a durable consumer's creation, only for the bus names of a module's kept data; the subjects its seat says and its hand-act log write; its - consumers of `node-backup`'s `item-started` and `item-ended`, each with the ordered - consumer §556 needs; its calls of `node-backup`'s `restore` as the caller `holder:mesh-recovery@same-node`, which + consumers of `node-backup`'s `item-started` and `item-ended`, filtered to those two events, each with the ordered + consumer §556 needs, from the process's `consumes`; its calls of `node-backup`'s `restore` as the caller `holder:mesh-recovery@same-node`, which `node-backup`'s definition names for `restore` and which the backup holder takes only for the recovery module's own item `points` (the operator, 13:02 CET); and nothing else. The bus names granted are those of every kept data of every catalog graph entry the catalog half keeps (§443) and of every entry a restore point names (an old section's name before a form change, a template's binding after), read by the grants part through - `kept-points`; no stream of the mesh's own (`CONTROL`, `NODES`, `EVENTS`, `DEAD_LETTER_NOTICES`, `DEAD_LETTERS`, + `kept-points` (§565); no stream of the mesh's own (`CONTROL`, `NODES`, `EVENTS`, `DEAD_LETTER_NOTICES`, `DEAD_LETTERS`, `PROV_APPLIED`, `CHECKS_READY`, `CHECKS_OTHER`, every `SEAT_` stream and every `KV_` bucket of a seat) is ever granted. Restricted streams (ADR 0436) are among the names, as ADR 0672 records. - **Bus names.** The recovery module turns a module and a kept data into bus names from the catalog graph entry of @@ -105,19 +111,20 @@ are written into the lines of G-12 they name.* it, so the server has at most two snapshots at once. No `restore-kept` starts while the dump runs, and no dump of the bus starts while a restore runs: the holder of `node-backup` of G says `item-started...` and `item-ended...` for every item, kept last per subject, 7 days, and puts off the bus's item - while a `restore-started` of the recovery module has no `restored`; the recovery module learns the bus module and + while a `restore-started` of the recovery module has no `restored`, the dump giving way when both start at once + (§563); the recovery module learns the bus module and its node from the seat list's definition of `mesh-broker` and the catalog half's claim of it, never a fixed name. Both read these streams as §556 has it, so a restart mid-dump or mid-restore misses neither. A blocked act waits, neither refused nor failing a want: past 60 minutes the recovery module writes `recovery-waits` and the backup holder `backup-waits`, each naming the item or the act it waits on, and `kept-points` lists the act that runs with since when and what it waits on. The recovery module consumes the item events that G's backup holder adds, an edge of kind 6 from that holder to it, so a backup holder of before G, which says none, is never relied on. - - **Its health.** Its declared health check calls `kept-points`, and fails while any aside's record ends at - `removed` (§516), when it also writes the finding `aside-unrestored` naming the module, the kept data and the - aside's `id`. + - **Its health.** Its declared health check calls `kept-points`, and fails only while an aside's record ends at + `removed` (§516) and no running act holds it, so a restore under way never fails it; it then also writes the + finding `aside-unrestored` (§561), which `recovery_restore_aside` (§566) clears. - **Its order.** The node-engine's `invokes` of its verbs gives the edge of kind 6 from the recovery module to the node-engine, so §298's first edge reads "the new version of the recovery module" (§555). At a revert of G the - edge runs backward (ADR 0656 §4): the node-engine goes back only after every module it restores there said + edge runs backward, as the order does when something goes away (ADR 0666 §14): the node-engine goes back only after every module it restores there said `restored` and runs (§298), and the recovery module, new in G, goes only after the node-engine, so it stays while any restore runs; its data item stays on its node, with its copies. - **Its tools** are named `recovery_`. @@ -172,7 +179,7 @@ are written into the lines of G-12 they name.* - §558 **A field of a served verb's answer is an edge of kind 6.** Kind 6 (§374) orders a module after a version that serves a verb or a word it uses, and a verb's presence alone says nothing of a field its answer gained, so a consumer relying on a new field of a served answer could run before the provider's new version and fall back in - silence (the grants part's first view relies on `binding` in `mesh-broker`'s `list`). An `invokes` entry therefore + silence (as the grants part's first view relied on `binding` in `mesh-broker`'s `list` before design 25's §9). An `invokes` entry therefore takes a long form, `{verb, fields}`: `verb` as today (`seat:.`), `fields` the names of its answer's fields the consumer relies on. The manifest validator refuses a field the seat's definition does not give that verb's answer, naming it. The delivery plan derives the edge of kind 6 from the holders of that seat to the @@ -184,14 +191,16 @@ are written into the lines of G-12 they name.* form, and the running checker, the indexer and the grants part, which read manifests through it, are rebuilt with it by the release's publish (kind 1), as for `max-mib`. That publish rebuilds every module importing mesh-sdk's `catalogue`, the set the release rebuilds already, so the form adds no rebuild. The consumers of G that name fields: - the grants part, `mesh-broker`'s `list` with `binding`; every reader of `mesh-catalog`'s `revision` through + every reader of `mesh-catalog`'s `revision` through mesh-sdk's client (the bus module, the status view, the condition keeper, the tool runner, the assigner, the grants part and the runner), with `none-yet` (§530); the status view, `mesh-catalog`'s `entry` with `manifest-digest` - (§506). The recovery module reads the manifest whole the entry carries (§443), a field of before G. Any other part of G relying on a field G adds to a served answer names it so in G's change, which the + (§506). The grants part names none: design 25 *bindings-first-view* §9 (on the branch + `design/25-bindings-from-the-list`) composes a binding from the fields `list` answered before G, so it relies on no + new field of `list`, and naming `binding` would close the circle of §569. The recovery module reads the manifest whole the entry carries (§443), a field of before G. Any other part of G relying on a field G adds to a served answer names it so in G's change, which the code review checks. Why: the module graph orders a consumer after what it relies on, and a field is part of the contract (lead-47's question from the operator's "the module graph is for this", 2026-11-11). How it is checked: unit - (manifest validator), a field the definition lacks refused; unit (delivery plan), the grants part ordered after - a bus module whose definition gives `binding`, and not before; unit (the release's reader), both forms read. + (manifest validator), a field the definition lacks refused; unit (delivery plan), a consumer naming a field + ordered after a holder whose definition gives it, and not before; unit (the release's reader), both forms read. - §559 **A bundle's finding writer publishes and hears through two runtime calls.** A bundle, in Go or TypeScript, holds no bus connection, so mesh-sdk's finding writer (§461, §503) in a bundle uses the runtime channel's existing calls, each gaining one argument: `mesh/publish` takes `ack: true`, and the tool runner then publishes the event on @@ -206,4 +215,5 @@ are written into the lines of G-12 they name.* languages over the one channel a bundle has, as §518 (develop question of 13:08 CET on §461). How it is checked: unit (tool runner), a publish with `ack` answered with its sequence and a refused one with `{error}`, a `deliver: new` subscription given no message older than it; whole (mesh-sdk, TypeScript and Go bundles), a finding written - and an ask answered through a tool runner of G. + and an ask answered through a tool runner of G; unit (mesh-sdk, both languages), the writer fails at once, naming + `finding-calls` and the node, against a tool runner that does not state it. diff --git a/03-DESIGN/01-to-be/47-milestones/G-15-develop-answers-fifth.md b/03-DESIGN/01-to-be/47-milestones/G-15-develop-answers-fifth.md new file mode 100644 index 00000000..39910ee6 --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-15-develop-answers-fifth.md @@ -0,0 +1,495 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-11 +decisions: + - "ADR 0672" + - "ADR 0668" + - "ADR 0667" + - "ADR 0666" + - "ADR 0664" + - "ADR 0626" + - "ADR 0619" + - "ADR 0596" + - "ADR 0595" + - "ADR 0573" + - "ADR 0564" + - "ADR 0539" + - "ADR 0500" + - "ADR 0429" + - "ADR 0404" + - "ADR 0283" + - "ADR 0259" + - "ADR 0235" +--- + +# 47, milestone G: develop's fifth batch, and round 3's notes on §516 to §559 + +One file of milestone G's part of [design 47](../47-delivery-from-commit-to-delivered.md); the part opens at +[G.md](G.md). Its design lines are numbered across every file of the part and cited as "design 47 G §n". + +*§560 to §584 answer develop's fifth batch of questions (lead-47's input of 2026-11-11, after round 3 of the review +of [G-12](G-12-develop-answers-fourth.md) and [G-14](G-14-recovery-module-and-review.md)), the reviewers' round-3 +notes on §516 to §559, and the knowledge base's review of ADR 0672 (the recovery module). Six carry the operator's +answers of 2026-11-11 at lead-47's terminal, each line saying so: §562 (the recovery module's move), §563 (a put-off +dump), §568 (the builder's retry on an event), §570 (refused asks listed) and §575 (a failed build's words). The +rest are of internal form only: they change nothing the operator sees, nobody's permissions and no data kept or +removed. A note that corrects a line of G-12 or G-14 is written in that line; here only what is new.* + +## The recovery module + +- §560 **Every event of the recovery module says its act, and its caller wakes on its own act.** (§554, §516, §551) + Every event of the seat `mesh-recovery` carries `act`, the id `snapshot-kept` or `restore-kept` answered with + `accepted`, or a hand tool's own act id (§566), and `at`, the UTC time it was said in RFC 3339. Each event's other + fields: + - `ended..`: `module`, `kept`, `form`, `fingerprint`, `snapshot`, `outcome` (`done` or `failed`), + `error` when failed, and on `done` the point whole (§516). + - `restore-started..`: `module`, `kept`, `form`, `fingerprint` and `snapshot`, said before the + restore reads the dump's item events and before any aside is taken (§563). + - `restored..`: `module`, `kept`, `form`, `fingerprint`, `snapshot`, `outcome`, `error`, `aside` and + `missing` (§516). + - `set-aside..` and `aside-discarded..`: the aside's `id`, `name` and `size`. + - `point-removed..` as §551 has it; and `expiry-ended`, a subject without parts, with `removed`, the + number of points the run removed, 0 included. + - **Every act ends with an event under its `act`.** `snapshot-kept` and `recovery_take` end with `ended`; + `restore-kept`, `recovery_restore` and `recovery_restore_aside` (§566) with `restored`; `recovery_aside` with + `set-aside`; `recovery_discard_aside` with `aside-discarded`; a run of §551's removal (at the start, once a day, + at a `snapshot-kept` or by `recovery_expire_points`) with `expiry-ended`, also when it removes nothing; + `recovery_retry` with the ending event of the act it asks again, under an `act` of its own; `recovery_cancel` + (§566) with the cancelled act's ending event, `outcome` `failed` and `error` `cancelled`. + - **An act a restart cut off** (accepted and written in the directory, never ended, §554) is ended at the module's + start, after the start's repair of asides (§516, §564), with its own ending event under its `act`, `outcome` + `failed` and `error` `stopped`. + - **A restore runs** from its `restore-started` until a `restored` with the same `act`; the backup holder reads it + so (§554, §563). + - **The caller wakes on its own act.** The node agent told `accepted` wakes only on an `ended` or a `restored` + whose `act` is the one it was given; a `form` and a `fingerprint` alone wake nothing, since a retried act's old + `failed` carries the same ones. Told `busy` with the running act's `act`, it wakes on that act's ending event + and asks again, and its new answer gives the `act` it waits on next. + - **The 90-minute bound** of the node agent's wait (§554) counts from its first ask for that point or restore, + every `busy` and every ask again within it. Past it, the node agent's have says the wait and the watchman's + `delivery-waits` raises it. + + This replaces §554's list of events. Why: an event a caller cannot tie to its own call wakes it on another's + failure, and an act whose end is never said holds its caller unseen (round 3, health and events, items 1 and 2; + running and testing, item 3). How it is checked: unit (mesh-recovery's contract), each event's fields read; whole + (recovery module), a retried act's caller not woken by the first act's `failed`, an expiry run that removes nothing + says `expiry-ended` with `removed` 0, an act cut off by a restart ends `failed` with `stopped`; unit (node agent), + `busy`, then the other act's ending event, then an ask again within one bound, and the bound passed after repeated + `busy` answers. +- §561 **The recovery module's and the backup holder's findings, each with its scope, id and clearing.** (§554) Each + is written by mesh-sdk's finding writer (§461) as the key `..` (ADR 0564 §2), each token escaped + as a key's name is (§73), and said on the part's finding stream (§383, §529): + - `points-filling`: the recovery module's, scope `mesh-recovery`, id the node it runs on. It is written when the + directory of its data item `points` passes the fill mark of its cap (§564), measured after each act and at each + of its disk readings, naming the size, the cap and the five largest points and asides. It is cleared when a + measure finds it below the mark. + - `recovery-waits`: the recovery module's, scope the module whose act waits, id the act's `act`. It is written + 60 minutes after the act began waiting on the dump of the bus's item (§563), naming the item and since when. It + is cleared when the act starts its work, or ends. + - `backup-waits`: the backup holder's, scope its node, id the item that waits (`.`, one escaped + token). It is written 60 minutes after the item was put off (§563), naming the restore's `act`. It is cleared + when the item starts again. + - `aside-unrestored`: the recovery module's, scope the module, id the aside's `id`. It is written when its health + check finds an aside whose record ends at `removed` and that no running act holds (§554). It is cleared when that + aside's record ends at `restored` or the aside is discarded. + + The recovery module and the backup holder of G are observers on the condition keeper's list of sources (§387, + §388), written into it in G's change. Why: a finding no one can clear stays open for ever, and the four findings + of §554 named no scope, id or clearing (round 3, health and events, item 4). How it is checked: whole (recovery + module, backup holder, the keeper), one whole test writes and then clears each: the points directory filled past + the mark, then an aside discarded; an act held by a dump past the bound (a setting, §557), then started; a dump + held past it, then started; an aside left at `removed` with no running act, then restored by + `recovery_restore_aside`. +- §562 **The recovery module moves as any module that holds data; its points and asides hold no move.** (§554) The + operator, 2026-11-11 about 14:15 CET at lead-47's terminal: the recovery module may be moved off its node at any + time, and the restore points and asides in its directory do not hold the move. A placement that moves it is made as + for any module that holds data. The assigner asks for the move a person must allow before it writes the want (ADR + 0539 §14). The old node's want is written as removed, and its data item stays on that node's disk, with its copies + in that node's repository, until the operator answers (ADR 0429 §6). On its new node the recovery module starts + with an empty directory: `kept-points` lists nothing of the old node's, and a `restore-kept` of a point left there + answers `missing`. Its `from-backup: yes` then asks the new node's backup holder, which holds no copy of it, so + `restored` says `failed` with `restore point missing` and §300 holds the revert. The placement verb asks the + recovery module nothing, and §554 no longer refuses a move while a point younger than 90 days is listed. Why: the + operator's answer (the knowledge base's review of ADR 0672, R6). How it is checked: unit (assigner), a placement + that moves the holder of `mesh-recovery` while points are listed is made, asked as ADR 0539 §14 has it; whole + (recovery module), on an empty directory a restore of an unknown point says `restore point missing`. +- §563 **When a dump and a restore start at one moment, the dump gives way.** (§554) Each side says its start before + it reads the other's: + - **The backup holder** of G, for the item of the holder of `mesh-broker` (read from the seat list's definition + and the catalog half's claim, §554), says `item-started`, then reads `mesh-recovery`'s `restore-started` and + `restored`, last per subject, once more. When a restore runs (§560), it ends the item at once with `item-ended`, + `outcome` `put-off` and `restore` naming that act, having read nothing of the bus, and starts the item again when + it hears `restored` with that `act`. + - **The recovery module** says `restore-started`, then reads the item events of the bus's item. While an + `item-started` has no `item-ended`, it waits, and takes no aside. + + Since each side says its start before it reads, at least one side sees the other, and the backup holder always + gives way, so the two never overlap. A dump that has run since before the restore began is waited for. The backup + holder ends every item of its own that it said started and never ended, at its start, after its read of §556, with + `item-ended`, `outcome` `failed` and `error` `stopped`, so no restore waits on a dump that no longer runs. Every + `item-started` and `item-ended` carries `at`, and `item-ended` its `outcome` (`done`, `failed` or `put-off`). + - **A put-off dump is that night's backup.** The operator, 2026-11-11 about 14:05 CET at lead-47's terminal: an + item ended `put-off` is neither a failed night nor a missed one (ADR 0235 §13, §21 hold): the item runs when + `restored` is heard, and the night is the outcome of that run. The item's last good backup ages as before, so + `backup-stale` is raised only by its own age rule, never for the put-off itself. `backed-up` (§302) lists a + put-off item with the restore's `act` and since when; `backup-waits` names it after 60 minutes (§561). + + Why: §554 left open which side gives way when both start at once (round 3, running and testing, item 1); how a + put-off item counts is the operator's answer (the knowledge base's review of ADR 0672, R5). How it is checked: + whole (backup holder, recovery module, the real bus), a dump and a restore asked at one moment never overlap and + the item runs after `restored`; a put-off item raises no `backup-stale` and is not counted failed; a backup holder + restarted during an item ends it `failed` with `stopped`, and a waiting restore then starts. +- §564 **The cap is a setting, an act that would pass it is refused before it removes anything, and a failed act + leaves no half-made point or useless aside.** (§554, §557) + - **The settings.** The cap of the data item `points` is the recovery module's setting `points-cap`, 10 GiB unless + set, and its fill mark the setting `points-filling-mark`, 80 % unless set, both read at its start, as §557 has a + bound. + - **Before it writes.** Before it writes anything, `snapshot-kept`, `recovery_take` and the aside of + `restore-kept` add up the bytes the bus states for each bus object they take. An act that would pass the cap + ends `failed` with `error` `cap`, naming the cap, that size and the directory's fill, before any aside is taken + and so before any live object is removed. A `snapshot-kept` that ends so holds the move as a failed backup does + (§296). A point or an aside that passes the cap while it is taken (the stream grew) ends its act the same way. + - **On a failure.** An act that ends `failed` removes its point under a temporary name (§516) then, not only at + the next start. An aside whose record ends at `set aside` (the live objects were never removed) is removed when + its restore fails or stops before the removal, and at the start, since the live data was never touched. + + Why: the cap is a bound a whole test sets (§557), and a restore refused for size after the removal would leave the + module's data only in an aside (round 3, running and testing, item 2 and its notes). How it is checked: whole + (recovery module, with small settings), a snapshot passing the cap ends `failed` and holds the move; a restore whose + aside would pass the cap is refused with the live data untouched; `points-filling` written past the mark (§561); a + failed point leaves no temporary name; an aside left at `set aside` by a stopped restore is removed at the start. +- §565 **The grants part reads the restore points' names through `kept-points`, and composes without them until it + answers.** (§554) The bus names of the recovery module's account include those a restore point names (§554). The + grants part reads them by its `invokes` of `seat:mesh-recovery.kept-points`, a kept call (ADR 0668 §1). It asks + again on every `ended` with `outcome` `done` of `mesh-recovery`, which it consumes (ADR 0668 §4), so a point's + names are granted before any restore of it. Until `kept-points` first answers, the grants part composes the + recovery module's account from the catalog half's kept entries alone (§443) and says so in `grants_first_views` + (§556). The edges between the two (kind 5, the grants part has acted for the recovery module's account; kind 6, + the recovery module serves `kept-points`) are a cycle of delivery edges, delivered as one step (§49). Why: the + account must follow the points without the grants part reading another part's state (ADR 0596; round 3, seams and + tools' notes). How it is checked: unit (grants part), an account composed from the catalog half alone while + `kept-points` is unanswered, and a point's old-section names added after an `ended`. +- §566 **Two more hand tools: put an aside back, and cancel a waiting act.** (§554, §516) Each is of class `act`, + with callers `operator` and `sessions`, checked by the identity of who asked (§554), written first in the hand-act + log of the seat `mesh-hand-acts` with the caller the bus proved and `why`, and an act of its own that runs one at a + time (ADR 0672): + - `recovery_restore_aside` (`module`, `kept`, `id`, `why`) restores an aside under the live names it was taken from, + for an aside whose record ends at `removed` that no running act holds (the case `aside-unrestored` names, §561). + It refuses, naming them, while live objects of those names exist, and it ends with `restored`. + - `recovery_cancel` (`act`, `why`) ends an accepted act that has not begun its work (one waiting on the dump, §563) + with its ending event, `outcome` `failed` and `error` `cancelled`. It refuses an act that has begun, naming its + step, since a restore cut off after its removal is put back only by its own failure path (§516). + + Why: a module's tools recover what a person may need to recover (*Tools are how a module is spoken to*; round 3, + seams and tools' notes). How it is checked: whole (recovery module), an aside left at `removed` put back by + `recovery_restore_aside` and refused while the live names exist; a waiting act cancelled and a running one refused. +- §567 **A restore point is checked by the digest of its bytes before it is restored.** (§516) A point's record + carries `digest`, the `sha256:` digest of each bus object's snapshot file, written when the point is renamed whole + (§516). `kept-points` lists it, and `ended` carries it with the point. Before a restore takes any aside, it computes + the digest of the bytes it is about to restore, whether from its own directory or brought back by the backup + holder (`from-backup: yes`). It compares that digest with the one `ended` carried when the seat's stream still + holds that point's `ended`, and with the record's otherwise. A mismatch ends the act `failed` with `restore point + damaged`, naming the point and the bus object, and §300 holds the revert. Why: a point brought back from the backup + was restored unchecked (round 3, security and upgrade's notes). How it is checked: whole (recovery module, backup + holder), a point whose file was altered after it was taken is refused before any aside is taken. + +## The builder + +- §568 **A transport fault in the builder's own read ends no build key, and a key's error says which kind of fault it + is.** (ADR 0664 §1 to §3, ADR 0668 §1 to §3; issue 1612, met again 2026-11-11) + - **The builder's own reads.** The builder's reads of the build seats' state `steps` (its own step keys, §467, + §481) are kept calls (ADR 0668 §1, §2). A read that meets a transport fault, of ADR 0664 §1's classes or the + read's own deadline, changes nothing the builder holds and writes no build key. The builder's watch of `steps` + takes up again from its last revision once the bus answers, and a read it made at its start is made again then. + Past five minutes it writes `call-unanswered` (ADR 0668 §3). A build key is written `failed` only from a step + key's end (ADR 0664 §3). + - **The words of a failed key.** A step that ended failed because its third run met a transport fault (ADR 0664 + §2) gives the build key an `error` that opens `transport fault:`, then the class (`no responders`, `name lookup`, + `connection` or `deadline`), the step's seat and node, and the cause of its last run in the build module's words. + Every other failure keeps its own opening (`not stored:`, `not pinned:`, `version taken:`, `refused kind:` or the + build's own words) and never opens `transport fault:`. The builder's read tools show the error whole, so a reader + tells a passing cause from a real one without the journal. + - **What builds such a key again.** The operator, 2026-11-11 about 14:15 CET at lead-47's terminal, decided by the + record of the builder's retry (number to come), which changes ADR 0664 §3: a key whose error opens `transport + fault:` is built again, once, as a new revision of the key (ADR 0429 §18), on the first event after its failure + that says its cause passed. Two events say so: the node-engine seat's `running..` saying `running` + for a module whose catalog graph entry claims the build seat the failed step ran on (§546), on any node; and the + node-engine's `manifest-reads-changed` of the node the failed step ran on (ADR 0668 §4), which it says when that + node is heard again. The builder hears both through its consumers of the node-engine seat, read as §556 has it. + It builds a key again at most once per event heard, and only while the key's newest revision is that failure, so + one event starts one revision. A revision that fails again on a transport fault is said as every failed key is, + its error opening `transport fault:` again, and waits for the next such event. A key failed for any other cause + is never built again on these events. `builder_build_again` (ADR 0668 §5) and the builder's next start build it + again too, as before. The builder's read tool of a key names the event that built its newest revision again. + + Why: a read the builder made met `no responders` and failed a key, which then stayed failed for about 45 minutes + (issue 1612, 2026-11-11), against ADR 0664 §3 (a key is not written failed while its step runs on); the retry on an + event is the operator's answer. How it is checked: unit (builder), a `no responders` read of `steps` leaves the key + `building` and the next revision ends it; a third-run transport fault's key error opens `transport fault: no + responders`, and a compile error's does not; a `running` of a holder of that build seat builds the key again once, + and a second `running` while the new revision builds starts nothing; a node's `manifest-reads-changed` builds again + a key failed on that node; a key failed on a compile error is not built again on either event. + +## The order of a change + +- §569 **A cycle with a build edge is refused, whatever its other edges, and a grant composed from a first view is an + edge.** (§49, §558, §374) The indexer's cycle rule (§49) reads every edge of the six kinds in one graph per watched + branch. A cycle whose edges are all delivery edges (kinds 2, 4, 5 and 6) is delivered as one step. A cycle with at + least one build edge (kind 1 or 3) is refused, whatever its other edges, at the check and at take-in, naming every + artifact on it in order with each edge's kind and the manifest word it came from: no build on such a cycle can + start before a delivery that waits for that build (ADR 0666 §14). Two kinds of edge enter that graph: + - **A seat verb a build calls.** When a build module's step calls a seat verb while it builds (the image build + module calling `container-image-registry`'s `mirror` for a base image), its manifest names it in `invokes`, and + the indexer derives an edge of kind 3 from that seat's holders to every artifact the build module builds that + the call serves (each image artifact that names a base image). + - **A grant from a first view.** A binding the grants part writes for a holder from its first view of a served + answer (design 25 *bindings-first-view* §4 and §9, being written on the branch `design/25-bindings-from-the-list`) + is an edge of kind 5 from the grants part to that holder. Where the grants part names a field of that answer + (§558), an edge of kind 6 runs from the answer's seat holder to the grants part. + + The circle of 2026-11-11 was such a cycle: the bus module's image waits, by kind 3, for the registry holder's + delivery; the registry holder waits, by kind 5, for its binding `mirrors` from the grants part; and the grants part + waited, by kind 6 on `mesh-broker`'s `list` with `binding`, for the bus module's new version. It is refused at its + check, naming the three artifacts. Design 25 *bindings-first-view* §9 removes the kind-6 edge: the grants part + composes a binding from the fields `list` already answers, so it relies on no new field and names none (§558). + G's delivery then puts the grants part before the bus module's build. Why: the circle mixed a build edge and a + delivery edge, and no rule saw it (issue 1966, notes 203 and 204; the operator, 2026-11-11 13:30 CET, on design + 25's §9). How it is checked: unit (indexer's graph), a cycle of kinds 3, 5 and 6 is refused naming its three + artifacts and their kinds; a cycle of kinds 5 and 6 alone is one step; a build module's `invokes` of `mirror` gives + a kind-3 edge to its image artifacts. + +## Asks to the operator + +- §570 **An ask the router refuses is heard by its asker, and no asker asks for longer than its level allows.** + (ADR 0259 §3, §4) The kind behind it is an asker left waiting on an ask the router never took (issue 1610; met + again 2026-11-11, when the bus module's three `nats_recreate` asks were refused at take-in and their tool kept + saying the operator had not answered). + - **The router says the refusal.** The router (the holder of `operator-channel`) says every ask it refuses at + take-in, for each cause ADR 0259 §4 gives as it holds, on that asker's + `mesh.seat.operator-channel.event.decided.`. The event carries `outcome` `refused`, the ask's `id`, `why` + in the router's words and `at`, as an ask's end without a warrant is said (ADR 0259 §3). + - **Every asker ends its request on it.** mesh-sdk's asker client gives its caller `refused` with `why` as soon as + it hears it. An asker's tool or act that waits on that ask answers `refused` with why, never "not answered yet"; + the bus module's `nats_recreate` and `nats_drop` are among them. + - **The lifetime is held where every ask is made.** mesh-sdk's asker client, through which every asker publishes + an ask, refuses before it publishes an ask whose expiry passes the bound of its highest option level (`approve` + 24 hours, `destroy` 10 minutes, ADR 0259 §4), naming the level and the bound. The bus module's keeper asks for + its level's bound in place of its fixed 12 hours (`AskLasts`). + - **The operator sees a refused ask.** The operator, 2026-11-11 about 14:15 CET at lead-47's terminal: the router + keeps each ask it refuses at take-in in its bucket `asks` under `.` as a closed record, with `outcome` + `refused`, `why` in the router's words and `at`, for 30 days like every closed record (ADR 0259 §3). The seat's + `asks` lists it with that outcome and words, as an ask that ended. + + Why: ADR 0259 §4 refuses such an ask on the decided subject and bounds a destroy option at 10 minutes, and neither + reached the asker; the list is the operator's answer. How it is checked: unit (mesh-sdk's asker client), a table + over the levels in which an expiry at the bound is published and one past it is refused before it is published, + and a `refused` that ends the request with why; the code review refuses an ask built outside the client; whole + (router, bus module), a refused ask is said on `decided`, `asks` lists it `refused` with the router's words, and + `nats_recreate` answers `refused` with those words. + +## A module's packages + +- §571 **A mesh package's entry in a lock file is written by the build, never by hand.** (ADR 0573 §2, §4; §47, + §478, §504) A module's `package.json` names a mesh package (one whose name §504 maps to a catalog artifact) with the + range `*`, or with the oldest version it accepts (ADR 0573 §2). Its committed `package-lock.json` may hold any entry + for that package, or none. + - **What the build writes.** The Node and TypeScript build module removes every entry of a mesh package from the + workspace's lock before it installs. It then writes the entry from the version the planner read and the + integrity the package registry gives for that version (ADR 0573 §2). In a delivery group's check where a member + publishes the package, it writes the entry from that member's head, packed for the check, with the packed + tarball's integrity (ADR 0573 §4, ADR 0500 §4). Every other entry is installed as the lock names it and checked + against its integrity (*Every dependency comes by the version its lock file names*). + - **Before its first publication.** A package of which no version is published yet (`npm-package-registry`'s + `versions` answers none, as for the packages G itself publishes first) is reached only in a group's check, from + its member's head. Outside a group, the edge of kind 1 (§47) builds the dependent after the package's first + publication, which G's delivery orders. + - **The check's lock rule** never refuses a mesh package's entry, nor its absence. + + Why: ADR 0573 §2 has the build write a mesh package's version into the workspace, and a lock made by hand holds + the integrity of a locally packed tarball, which the published one never matches (develop question of the forge + module's developer, 2026-11-11). How it is checked: unit (Node build module), a lock naming a local tarball's + integrity for a mesh package is built with the registry's version and integrity; a lock without the entry is built + alike; a non-mesh entry with a wrong integrity is refused; whole (G's group check), the forge module's tools are + built against the head of the member that publishes the package, packed. + +## The builder's forms + +- §572 **The builder's process answers a package's name, and its tools ask it.** (§478, §504) The mapping from a + package's name to its module and artifact is the builder process's own, from its view of the catalog half (§348). + The seat `mesh-builds`' `reserved`, given `name`, is answered by the builder's process from that view. The + builder's tools bundle holds no catalog view: `builder_versions`, asked by a name, calls `reserved` (or, for a + module, the builder's own read verb) through its `invokes` and answers what the process answers. Why: one mapping + and one view, in the part that keeps them (ADR 0596 §2; develop question BL7's name part). How it is checked: unit + (builder), `reserved` with an npm name and with a Go module path answered from the view; whole (builder), + `builder_versions` by name answers the same as `reserved`. +- §573 **The build seat's ask carries `version`.** (§370, §458) The ask of each build seat (`node-build-go`, + `node-build-node` and the image build seat) gains the field `version`: the version `MAJOR.MINOR.PATCH` the builder + reserved for the build (§369), or empty for a build that reserves none (§371, §373). The field is part of the build + seats' contract package, beside the ask's other fields. The builder fills it when it asks, and each build module + reads it and passes it as `MESH_VERSION` (§370), passing none when it is empty, so the build stamps none (§458). A + build module of before G ignores the field and stamps nothing. Why: §370 named no field (develop question of the + builder's developer on CT5). How it is checked: unit (build seats' contract), `version` read and an empty one + read as none; whole (Go and TypeScript build modules), a build asked with a version carries it. +- §574 **`toolchain-agreed` carries `digests` as a list.** (§472, §495) The event's field `digests` is a list, each + item `{digest, nodes}`: the `sha256:` digest of the toolchain program and the nodes that reported it at the agreed + version, sorted by digest, with each item's `nodes` sorted. It replaces the field `digest`, which the event no + longer carries. Why: §472 names one digest per group of nodes, and a list keeps two digests of one version apart + (develop question of the contracts developer). How it is checked: unit (mesh-builds' contract), the list read, and + an event carrying `digest` alone refused. + +## What a node-engine says of a build + +- §575 **A want whose build is not done says how the build stands.** (§524) The operator, 2026-11-11 about 14:05 CET + at lead-47's terminal: where the node-engine's have says that a want's build is not done, it says how the build + stands, read from the build key it already reads for the want's artifacts. A failed build reads `the build of + at failed: `, the error whole, `transport fault:` included (§568). A + build that runs reads `the build of at is being built`. The have's phase is `held`, and its + `wait-held` carries the same words, which the watchman's `delivery-waits` names. "Is not done" is said no more. Why: + the operator's answer; a failed build said as not done left the operator and the watchers waiting for a build that + had ended. How it is checked: unit (node-engine), a want whose build key is failed says `failed:` with the key's + error, and one whose build runs says `is being built`. + +## The tool runner, and kept data at run time + +- §576 **A bundle reads its tool runner's capability words through one call.** (§488, §545, §559) The tool runner + answers the runtime call `mesh/capabilities` with `{words}`, the capability words it states (§545). mesh-sdk's + bundle, in Go and in TypeScript, makes that call once, at its first call that needs a word, and keeps the answer + for as long as it runs. A bundle lives no longer than the tool runner that started it (its channel is that tool + runner's standard input and output), so after a restart of the tool runner the bundle is started again and reads + the words again. A tool runner of before G answers `mesh/capabilities` as an unknown call, which the bundle reads as + no word stated, so a call of a family G adds fails at once, naming its word and the node (§488). A bundle learns + its node from `MESH_NODE`, which the tool runner sets in the environment of every bundle it starts, read alike in Go + and in TypeScript. Why: §488 named no channel (develop question of the mesh-sdk developer). How it is checked: unit + (tool runner), `mesh/capabilities` answers the stated words; unit (mesh-sdk, both languages), the words read once, + an unknown-call answer read as none, and the node read from `MESH_NODE`. +- §577 **A part reaches the kept data its `requires` declares through its binding, never by an old name.** (§529; + ADR 0595 §1, §9; ADR 0619 §2) + - **A bundle** reaches it only through the tool runner's `mesh/` calls by its local name (ADR 0595 §9), which the + tool runner resolves in the module's membership binding. + - **A process with an account of its own** reaches it by its binding: mesh-sdk's process package gains `Binding`, + which takes a local name and gives the entry's bus names (bucket, stream and subjects, object stores). It asks the + seat `mesh-broker`'s `show` for the module and the local name at the process's start, as a kept call (ADR 0668 §1) + that the process's `bus` names in `invokes`. It asks again when it hears `mesh-broker`'s `recreated` or `dropped` + for that entry, which the process consumes. Until `show` answers, the process acts on nothing that needs the entry + and its health says it waits, naming the entry (ADR 0283). + - **The old name** (mesh-sdk's opening of an essential bucket by its declared name) is for a foundation part's old + `state` section alone (ADR 0619 §2). A part that declares an entry in `requires` opens it by `Binding`; the forge + bridge's `performed` is among them, moved so in G's change. + - **The validator's rule for the finding words** (§529) accepts the old stream `-findings` only for a part + on the foundation list (ADR 0619 §3 as ADR 0666 §4 changes it). It refuses that stream for every other part, whose + finding stream it requires in the template form: `finding-events`, by the template `events`, its entry listing + `finding-written.*`, `finding-cleared.*` and `findings-stated.*`. + + Why: a part's kept data is the bus module's to name and the part's to ask for (ADR 0595 §1, §7), and §529 left the + run-time path and the validator's row open (develop question of the watchman's developer). How it is checked: unit + (mesh-sdk's process package), `Binding` answered from `show`, asked again on `recreated`, and waiting while + unanswered; unit (forge bridge), `performed` opened by its binding; unit (manifest validator), a foundation part's + old finding stream accepted, another part's refused and its template form accepted. + +## The indexer's forms + +- §578 **`manifest-digest` carries its prefix, and the key does not.** (§506, §445) The catalog graph entry's field + `manifest-digest` is `sha256:` followed by the 64 hex digits, as every digest a record names. The module's value + and §445's `manifests` key stay the 64 hex digits as on main, since a key's name takes no `:` (§73), and mesh-sdk's + catalog client gives the bare digits as today. The indexer writes all three from the one digest it computed at take-in. + Why: one digest in two forms, the field's as the records write a digest and the key's as a key's name allows + (develop question of the indexer's developer). How it is checked: unit (indexer), the entry's field after its + prefix equals the key's name and the module's value. +- §579 **The indexer keeps the records pinning each revision, and removes a revision only on an event.** (§153, + §178, §216) + - **Where.** The indexer, a foundation part, keeps its old `state` section `pins`: one key per catalog graph + revision, `.` (the watched branch and the revision's id, each escaped as a key's name is, §73), + whose value is the list of delivery record ids that pin it. A list of ids is kept rather than a count, so a + `pinned` heard twice pins once. At about 20 revisions pinned a day for 90 days, about 1,800 keys of about 0.2 KiB + each, it is capped at 4 MiB (an estimate). + - **Applying `pinned` and `freed`.** The indexer adds or removes a record's id by compare-and-set, and only then + acknowledges the event on its durable consumer of `mesh-delivery`'s events (§153). A `freed` said while it was + stopped is applied when it comes back, and one applied twice changes nothing. + - **Current.** A revision is current while it is the newest revision the indexer took in on its watched branch, + whichever watched branch that is, or the newest revision of an open candidate (its `candidates.`). It stops + being current when a newer revision of its branch is taken in, or when the candidate's `heads-replaced` is heard + (its heads replaced, or its pull request closed) or the candidate is taken in. + - **Removal.** When a revision's list is empty and it is not current, the indexer removes its revision object and + its key, and its entry objects as §179 has it. It removes one only on applying `freed` or on the revision ceasing + to be current, never on a timer. + - **Inputs.** A catalog graph revision names no input revision. "The input revisions it names" (§178) are an + assignment graph revision's catalog revisions, which a delivery record pins itself (§153), so the indexer keeps + each by its own list alone. The assigner keeps its pins of assignment graph revisions in the same form, in its own + kept data in the form §529 gives its part. + - **Its tools.** `indexer_pins` reads each kept revision with the records that pin it, whether it is current and + why, and the store's fill against its cap. `indexer_consumer` reads each of the indexer's durable consumers: its + stream, name, acknowledged sequence, the messages pending, and the last event applied with its `at`. Both are + of class `read`. + + Why: §153 and §178 said what is kept, not where or how it is counted (develop questions IX17 and IX18). How it is + checked: unit (indexer), a revision pinned twice by one record and freed once is removed; a `freed` applied twice + changes nothing; a revision of a second watched branch kept while current; a candidate's revision removed on + `heads-replaced`; whole (indexer, gate), a `freed` said while the indexer was stopped is applied after its start. + +## Observers with more than one bundle, and the forge module's forms + +- §580 **Each finding kind belongs to one bundle, and a statement covers only its own kinds.** (§383, §384, §485) + A module whose bundles write one state `findings` (the forge module has four: its TypeScript tools, its Go package + registry, its forge and its agent token) gives each finding kind to exactly one bundle. That bundle's mesh-sdk + writer is made with the kinds it owns, and refuses a `write` or a `clear` of any other kind, naming it. Each bundle + judges and states its own kinds from its own truth (§384); no bundle reads the whole state to state another's + findings. Its `findings-stated` carries `kinds`, the kinds it states, and the writer's statement deletes only keys of + those kinds. The keeper sets its view of that observer, node and set of kinds from the statement, and clears only + conditions of those kinds that the statement does not name. A statement with no `kinds` (a writer of a part with + one bundle) covers every kind, as before. Why: a statement that deletes every key it does not state would clear + another bundle's findings (develop question of the forge module's developer, GIT14). How it is checked: unit + (writer, both languages), a statement with `kinds` deletes no key of another kind, and a write of another kind is + refused; whole (keeper), two bundles' statements on one node each keep the other's conditions open. +- §581 **A `seen` key's 30 days are judged on each step end, with no timer.** (§487) The forge module writes `at` in + each `seen` key's value, the time of the `step-ended` that wrote it. On each `step-ended` it handles, and at its + start, it deletes the `seen` keys whose `at` is more than 30 days old, at most 100 for each event, the oldest first, + so the bus keeps no key time-to-live and no template option is added. The forge module being a foundation part + (§584), its state `failed-steps` is in its old `state` section, and a bundle reaches it through the tool runner's + state call by its name, as every old section is reached. Why: §487's 30 days needed + a trigger, and an event the module already handles is one (develop question of the forge module's developer). How + it is checked: unit (forge module), a `seen` key older than 30 days is deleted at the next `step-ended`, and a + younger one is kept. +- §582 **`reserved` by name answers a released version from the catalog.** (§381, §478, §369) Given `name` and + `version` with no reservation of that version (§369 deletes a reservation once its version is released), the + builder finds the module from the name (§572) and asks the seat `mesh-catalog`'s `versions` for that module, as a + kept call in its `invokes`. When the catalog holds that version released, `reserved` answers `released` with the + artifacts and the digests the release recorded, and the registry holder answers its publish as held when the + digest is the release's, storing nothing (§381). A version neither reserved nor released, or a name no entry + carries, is answered `error`, naming which, and the holder refuses the publish. Why: the holder knows only the name, + and the module is the builder's own mapping (develop question of the forge module's developer). How it is checked: + unit (builder), `reserved` for a released version answers `released` with its digests, and an unknown version + `error`; whole (registry holder, builder), a publish of a released version at its digest stores nothing. +- §583 **A manifest's first `version` is §368's.** The `version` G writes into the forge module's and the image + registry module's manifests is the `MAJOR.MINOR` of the version §372 fills for each, read through + `builder_versions`, and `0.1` for a module with none published (§368); their old values (`"1"` and `"2"`) carry + nothing over. Why: §368 answers it (develop questions GIT15 and MAN1). How it is checked: as §368. +- §584 **The forge module and the image registry module are foundation parts.** (§6, §529, §569; ADR 0619 §1 to §3) + The operator, 2026-11-11 about 14:30 CET at lead-47's terminal: ADR 0619 §1 is followed as written. The bus + module's build calls both before the bus module can answer, the forge for its source and the image registry's + `mirror` for its base image, so both are foundation parts. + - **The list.** The manifest validator's list (§6, ADR 0619 §3) gains the holder of the seat `git` (the forge + module, `gitea`) and the holder of `container-image-registry` (the image registry module, `distribution`) in G's + change. + - **Their kept data.** Each keeps its kept data in its old sections (ADR 0619 §2): its state `findings` and its + finding stream `-findings` as §529 has a foundation part declare them, and every other state in its old + `state` section, the forge module's `failed-steps` among them (§581). The image registry module's `holds`, + `windows` and `mirrors` go back from their template entries to its old `state` section under the names they had + there. All three are empty on the bus (no key was ever written), so nothing is carried. Their template entries, + no longer declared, become orphans that `orphans` lists, removed only by the operator's `drop` (ADR 0595 §4, §5), + as §529 has it for the healers' old stream. + - **The delivery order.** The running image registry, of its build before G, serves `mirror` only once its bindings + are composed, and both the bus module's new build and the image registry's own new build ask it for their base + images. So the grants part with design 25 *bindings-first-view* §9 is delivered first, alone, and composes the + running registry's three bindings from `list`'s fields (on the branch `design/25-bindings-from-the-list`). Then + the image registry's and the bus module's builds of G are built through the running registry's `mirror` (edges of + kind 3, §569), and delivered by the edges of §49. Once the image registry of G runs, its state is in the old + sections, which the grants part creates as it creates every foundation part's, and no binding of a first view is + needed for it. + + Why: the operator's answer; the list and the order follow ADR 0619 §1 and §3 and §569. How it is checked: unit + (manifest validator), both manifests of G's heads accepted with their old sections, and a template entry in either + refused; the catalog's validation at G's heads refuses neither; the delivery plan of G puts the grants part's + delivery before both builds. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index 065bee02..016ffa52 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -5,6 +5,7 @@ design: 47 milestone: G updated: 2026-11-11 decisions: + - "ADR 0500" - "ADR 0672" - "ADR 0436" - "ADR 0670" @@ -67,6 +68,8 @@ decisions: - "ADR 0283" - "ADR 0253" - "ADR 0240" + - "ADR 0283" + - "ADR 0259" - "ADR 0235" - "ADR 0233" --- @@ -79,7 +82,7 @@ file on 2026-11-10, and is cited as "design 47 G §n". A sentence that holds a d stayed in the main file, and G builds what it says from G's delivery. A section named in italics is in this part, or, when it is not here, in the main file. -The part is fourteen files, each read whole, its design lines numbered across them: +The part is fifteen files, each read whole, its design lines numbered across them: - [G-1-pipelines-registration-and-steps](G-1-pipelines-registration-and-steps.md): pipelines per module: registration, watched branches, steps and policy, what holds a delivery, approval, groups, dependencies, a publish. - [G-2-pipelines-sizes-checks-and-tools](G-2-pipelines-sizes-checks-and-tools.md): pipelines per module: any size, the pipeline makes the check, who may say what, health, recovery, tools, kept data, how G itself is delivered, the choices under ADR 0666 and ADR 0667. @@ -95,3 +98,4 @@ The part is fourteen files, each read whole, its design lines numbered across th - [G-12-develop-answers-fourth](G-12-develop-answers-fourth.md): the operator's answers of 2026-11-11 noon, round 3's leftovers and develop's fourth batch of questions (§516 to §553). - [G-13-review-round-3](G-13-review-round-3.md): the review of §480 to §515, rounds 2 and 3 (§600 on). - [G-14-recovery-module-and-review](G-14-recovery-module-and-review.md): the recovery module, the operator's answer of 2026-11-11 12:55 CET, and the review of §516 to §553 (§554 to §559). +- [G-15-develop-answers-fifth](G-15-develop-answers-fifth.md): develop's fifth batch, round 3's notes on §516 to §559, the knowledge base's review of the recovery module's record, and the operator's answers of 2026-11-11 about 14:05, 14:15 and 14:30 CET (§560 to §584).