From 8999f8e9d046129ece63dfb09b2d8ad25fd9a244 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 14:15:17 +0100 Subject: [PATCH 1/3] Answer design 47 G's fifth develop batch, so develop can go on without choosing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writes §560 to §584 (G-15) and corrects the lines of G-1, G-12 and G-14 those answers name. They cover the recovery module's events, findings, cap, hand tools and digest check; the dump giving way to a restore; the builder's reads and its retry on an event; mixed build and delivery cycles; refused asks; mesh packages in lock files; and the forms the developers asked about. They carry the operator's answers of 2026-11-11 (14:05, 14:15 and 14:30 CET): a put-off dump is that night's backup; the recovery module moves freely; it is one process holding its seat; refused asks are listed; a failed build says so; and the forge and the image registry modules join the foundation. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- 00-META/glossary.md | 2 +- .../47-delivery-from-commit-to-delivered.md | 3 +- .../G-1-pipelines-registration-and-steps.md | 9 +- .../G-12-develop-answers-fourth.md | 30 +- .../G-14-recovery-module-and-review.md | 68 ++- .../G-15-develop-answers-fifth.md | 495 ++++++++++++++++++ 03-DESIGN/01-to-be/47-milestones/G.md | 6 +- 7 files changed, 569 insertions(+), 44 deletions(-) create mode 100644 03-DESIGN/01-to-be/47-milestones/G-15-develop-answers-fifth.md 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). -- 2.54.0 From e5d199263ba592a87663cb1518bc91f98f30a389 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 14:20:10 +0100 Subject: [PATCH 2/3] Make the forge module's readings of step-ended design lines, and home the reserved client in its seat's contract Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../G-15-develop-answers-fifth.md | 36 ++++++++++++++++++- 03-DESIGN/01-to-be/47-milestones/G.md | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) 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 index 39910ee6..93602658 100644 --- 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 @@ -30,7 +30,7 @@ decisions: 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 +*§560 to §586 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 @@ -493,3 +493,37 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he (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. +- §585 **What the forge module reads from `step-ended`, and the form of its `failed-steps`.** (§39, §475, §487, §581) + The forge module's readings where no line spoke, made design lines: + - **The fingerprint** of a step is the body's `fingerprint`, the build-input fingerprint, which the runner fills + in every `step-ended`. No field of the step's `result` is read for it, and a `step-ended` without it is written in + the forge module's journal and changes nothing. + - **A failure** is a `step-ended` whose `outcome` is `failed` and whose `status` is `required`. An `optional` or + `bypass` step's failure, and a `could not run`, record no failure, as ADR 0666 §11 refuses only a failed + `required` step. + - **A rerun's pass** clears a failure when the body says `rerun: true` and its `by`, the caller the runtime proved + as the runner's verb `rerun` names its callers, is `operator` or `seat:mesh-healers`; any other `by` clears + nothing (§39). + - **The watched branches** are those of each registration whose git origin is a repository the forge holds, per + repository, read through `mesh-catalog`'s `registrations` and kept current on `registration-changed`. + - **A `seen` key** `seen..` holds `{v, commit, module, fingerprints, at}`: `v` the form's version + (1), `fingerprints` every build-input fingerprint the runs named for that commit and module, and `at` as §581 + has it. Both key forms of `failed-steps` are defined in the seat `git`'s contract, in the forge module's + contracts package. + - **The fill** of `failed-steps` that its read tool shows is counted by the forge module from the keys and value + bytes it reads, against the state's cap of 64 MiB, and said as counted, since a bundle's state call gives no + fill of the bus's own. + + Why: the forge module's developer read these where no line spoke (develop, batch 5); each follows §39, §475, §487 + and ADR 0666 §11. How it is checked: unit (forge module), a `required` failure recorded and an `optional` one not; + a rerun pass by `operator` clears and one by another caller does not; a `seen` value read in its form; unit (the + seat `git`'s contract), both key forms read. +- §586 **The client of `reserved` is the seat `mesh-builds`' contract package.** (§381, §478, §582) The forge + module's package registry and the image registry module both ask `reserved`. The call is the seat `mesh-builds`' + contract, so its client lives where ADR 0573 §1 puts a seat's contract: the package the seat's holder's + repository publishes, mesh-delivery's contracts package, which defines `mesh-builds` (§52). That package gains a + `mesh-builds` client with `reserved`, its input and its answers (`reserved`, `released`, `error`), and both + registry holders use it in place of their own copies. It is not put in mesh-sdk, which keeps no seat-specific + verb (ADR 0573 §1). Why: code two modules share has one home, and for a seat's verb that home is the seat's + contract package (develop question of the forge module's developer). How it is checked: unit (mesh-delivery's + contracts), the client's input and each answer read; the code review finds no second copy in either holder. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index 016ffa52..2b952a31 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -98,4 +98,4 @@ The part is fifteen files, each read whole, its design lines numbered across the - [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). +- [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 §586). -- 2.54.0 From 785cd229b310a1c771863c793d56437c0adbc5cb Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 14:35:30 +0100 Subject: [PATCH 3/3] Fix batch 5 by its review, so no cycle, lost act or lost pin is left to develop Writes round 1's musts into the lines they name. A build edge is met by a running holder. An act cut off by a move ends through its failure path. Pins survive a race and a restart, and the gate restates them. Each point's digest is kept on the bus. The words of a failed build move to the assigner's hold. Also writes the operator's answers of 14:55 and 15:15 CET: three automatic rebuilds and then an ask; the image registry copies its template keys at its first start; refused asks kept are a setting. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- 00-META/glossary.md | 2 +- .../G-14-recovery-module-and-review.md | 3 +- .../G-15-develop-answers-fifth.md | 258 ++++++++++++------ 03-DESIGN/01-to-be/47-milestones/G.md | 1 - 4 files changed, 184 insertions(+), 80 deletions(-) diff --git a/00-META/glossary.md b/00-META/glossary.md index 80130957..09d27f4e 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 hand tool `recovery_discard_aside`. + **aside** — live kept data a restore set aside, removed only by the hand tool `recovery_discard_aside`, which the operator and sessions call. *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-milestones/G-14-recovery-module-and-review.md b/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md index c8fba9cf..2ad4c5b9 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 @@ -194,8 +194,7 @@ are written into the lines of G-12 they name.* 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 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 + (§506). The grants part names none: design 25 *bindings-first-view* §9 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 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 index 93602658..88b9ad61 100644 --- 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 @@ -30,13 +30,12 @@ decisions: 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 §586 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.* +*§560 to §586 answer develop's fifth batch (lead-47's input of 2026-11-11), the reviewers' round-3 notes on §516 to +§559, the knowledge base's review of ADR 0672 (the recovery module), and round 1 of this batch's own review, written +into the lines it names. Seven carry the operator's answers of 2026-11-11 at lead-47's terminal, each line saying +so: §562 (the module's move), §563 (a put-off dump), §568 (the builder's retry), §570 (refused asks), §575 (a failed +build's words), §584 (the forge and image registry in the foundation) and §554's account. The rest are of internal +form only. 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 @@ -71,29 +70,49 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - **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. + - **How the node agent hears and keeps it.** The node-engine's manifest `consumes` `mesh-recovery`'s `ended` and + `restored`, read as §556 has it. Before it asks, the node agent writes in its have the want's wait with `ask-at` + (its first ask's time); once answered `accepted`, it writes the `act` there too. After a restart it reads that + have: an `act` whose ending event the read of §556 already gives is taken as heard, and one not yet ended is + waited on, asked nothing again; a wait with no `act` yet is asked again within its bound. + - **What the node agent does with each ending.** + + | outcome, `error` | what the node agent does | + |---|---| + | `done` | goes on with the move or the revert | + | `failed`, `stopped` (a restart or a move cut the act off, §562) | asks again, within the same bound | + | `failed`, `cancelled` (§566) | asks nothing again; holds the move, the reason `cancelled` with the hand act's `why` | + | `failed`, `cap` (§564) | holds the move, the reason the error's own words | + | `failed`, `restore point missing` or `restore point damaged` (§516, §567) | holds the revert as §300 has it, the reason the error's own words | + | `failed`, any other error | holds the move, the reason the error's own words | + + A hold is the phase `held` with that reason, said as `wait-held` (§541), and ends on a new want or the + operator's hand act (`recovery_retry`, then a new ask of the node agent on that act's ending). 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. + `busy`, then the other act's ending event, then an ask again within one bound, the bound passed after repeated + `busy` answers, and each row of the table; whole (node-engine, recovery module), a node-engine restarted during a + restore takes that restore's `restored` and asks nothing again. - §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 + directory of its data item `points` passes the fill mark of its cap (§564), which the recovery module measures + itself at its start, after each act and after each expiry run, 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 + token). It is written 60 minutes (its setting `put-off-bound`, §557) 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-unrestored`: the recovery module's, scope the module, id the aside's `id`. It is written at an act's end + and at the module's start for each aside whose record ends at `removed` and that no running act holds, which + the health check also fails on (§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, @@ -112,10 +131,26 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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`. + recovery module nothing, and §554 no longer refuses a move while a point younger than 90 days is listed. + - **An act a move cuts off.** When its node's want is written as removed, the old instance takes no new call + (answering `error` naming the move) and, before it stops, ends its running act through its failure path (§516): + a restore past its removal puts the aside back under the live names and sets the consumers back; then it ends + every accepted act, the running one included, with its ending event, `outcome` `failed` and `error` `stopped`. + - **A stop it could not finish** (the old node gone or killed): the new instance, at its start, reads the seat's + stream as §556 has it, and ends `failed` with `stopped` every act whose `restore-started` or acceptance (its + `ended` or `restored` missing) the stream holds with no ending event, and writes `aside-unrestored` for each such + restore, naming the old node, since its aside is on the old node's disk. + - **Who asks again.** The node agent and the backup holder hear those endings (§560, §563); the node agent asks + again the new holder, which it finds from the catalog half's claims of `mesh-recovery`, and the backup holder + starts a put-off item again on a `stopped` `restored`. + + Why: the operator's answer (the knowledge base's review of ADR 0672, R6); the ending of a cut-off act is the + design's (round 1 of this batch's review, running and testing R2, health and events H1). 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`; a move during a restore after its removal leaves the live data as before and ends the act `stopped`; a + new instance whose old one was killed during a restore ends that act `stopped` and writes `aside-unrestored`; a + put-off dump starts again after it. - §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 @@ -125,6 +160,8 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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. + - **Said means acknowledged.** Each side reads the other's only once the bus acknowledged its own start event on + its stream. 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 @@ -139,8 +176,9 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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 + unit (backup holder and recovery module against a fake bus), both orders of "say, then read" in both sides, each + ending with no overlap; 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) @@ -152,6 +190,8 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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. + - **Brought back.** A point brought back from the backup (`from-backup: yes`, §516) is not counted against the + cap: it lies beside its path only while its restore runs, and is removed when the restore ends. - **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. @@ -164,21 +204,25 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - §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, + again on every `ended` with `outcome` `done`, every `point-removed` and every `expiry-ended` 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 after a start, the grants part keeps the names it last granted from it, kept in its state + `memberships` with the account, and adds the catalog half's kept entries (§443); it takes no name away before + that answer, and says the wait 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`. + tools' notes; running and testing R3). How it is checked: unit (grants part), a restarted grants part with + `kept-points` unanswered keeps a point's names it granted before, and a point's old-section names are added after + an `ended` and taken away after its `point-removed`. - §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`. + It refuses, naming them, while live objects of those names exist (an aside taken by `recovery_aside` is put + back by this tool too, the refusal naming `recovery_aside`'s aside when the names are live), 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). @@ -188,13 +232,17 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he `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 + (§516). The same digests are kept on the bus, apart from the bytes: in the recovery module's own state `digests` + (its kept data, by the template `state`), one key per point, its `snapshot` escaped as a key's name is (§73), + written before `ended` is said and deleted when the point is removed (§551); about 0.3 KiB a key, capped at 4 MiB. + `kept-points` lists the digests, and `ended` carries them 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`), and compares it with the key in `digests`; the record in the directory is never + the reference. The state holds digests only and lists nothing, so §517's "no `bus-points` state" stands for the + list. 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. + holder), a point whose file and record were both altered after it was taken is refused before any aside is taken. ## The builder @@ -221,9 +269,21 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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 + its error opening `transport fault:` again, and waits for the next such event. The builder's manifest `consumes` + both events. + - **Three automatic builds, then the operator is asked.** The operator, 2026-11-11 about 14:55 CET at lead-47's + terminal: the builder counts, in the key's newest revision, the revisions events started since the last one a + person started (`event-builds`). After three that failed, the next such event starts nothing: the builder asks + the operator once, through the seat `operator-channel`'s ask (level `approve`, options *Build once more* and + *Leave it*, `about` the key so a newer ask replaces it, ADR 0666 §12), naming the module, the fingerprint and + the last error whole. *Build once more* builds one revision, counted as a person's, so the count starts again; + *Leave it*, a decline or the ask's expiry leaves the key failed until `builder_build_again` or a new + fingerprint. The failure stays said throughout. 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. + again too, as before. The builder's read tool of a key names the event that built its newest revision again, + `event-builds`, and the open ask. + - **Before develop.** These lines rest on the record of the builder's retry, which changes ADR 0664 §3 (in force); + it is accepted before G's builder is built from them, and its number replaces "number to come" here. 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 @@ -231,7 +291,9 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he `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. + a key failed on that node; a key failed on a compile error is not built again on either event; after three failed + event-started revisions the fourth event asks the operator once and builds nothing, and *Build once more* builds + one. ## The order of a change @@ -243,23 +305,28 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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). + the indexer derives an edge of kind 3 from that seat's holders to every image artifact the build module builds. + The edge is met by the holder that runs when the build starts, whatever its version: it orders a build after a + running holder, never after the holder's new version. So the holder's own artifacts are built through its + running version, and the edge makes no cycle through them. - **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`) + answer (design 25 *bindings-first-view* §4 and §9) 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. + (§558), an edge of kind 6 runs from the answer's seat holder to the grants part. Its new version must run, so this + edge is met only by that version. 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 + waited, by kind 6 on `mesh-broker`'s `list` with `binding`, for the bus module's new version: the kind-3 edge waited + for a registry that served `mirror`, which needed its binding, which needed the new bus module. 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. + a kind-3 edge to its image artifacts; the image registry's own image, built through its running holder, is not + refused. ## Asks to the operator @@ -274,20 +341,26 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - **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 + - **The lifetime is held where every ask is made.** mesh-sdk's asker client changes in G's mesh-sdk release, which + every asker is built against (§47). 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 + `refused`, `why` in the router's words and `at`, for 30 days like every closed record (ADR 0259 §3). It writes + the record only by a create: when `.` holds a record already (an open ask, its tickets with it), the + refusal writes nothing there and is said on `decided` alone. The operator, 2026-11-11 about 15:15 CET ("just make + it configurable"): the router keeps at most `refused-kept` refused records per asker, a setting of the router, + 20 unless set, removing the oldest first, each also gone after its 30 days, each removal written in its journal. 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 + (router, bus module), a refused ask is said on `decided`, a refusal of an id that is open leaves the open record and its tickets as they + were, a twenty-first refusal of one asker removes its oldest, `asks` lists it `refused` with the router's words, and `nats_recreate` answers `refused` with those words. ## A module's packages @@ -307,6 +380,8 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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. + - **The reuse key** of a build takes the versions it wrote for mesh packages, as the fingerprint takes them (ADR + 0573 §2), so a build is never reused across two of them. 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 @@ -321,7 +396,7 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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 + module, `reserved` with `module`) 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`. @@ -336,21 +411,26 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - §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 + longer carries; a kept event of before G that carries `digest` alone is read as + one item with no `nodes`. 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. + an event of before G carrying `digest` alone read as one item with no `nodes`, so a reader of G refuses none + and nothing waits for the next agreement. ## 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`. +- §575 **A want held for its build says how the build stands.** (§524) The operator, 2026-11-11 about 14:05 CET at + lead-47's terminal: the words "the build of at is not done" are the assigner's, the reason + of its hold under its first rule (no want for a fingerprint whose build is not done, ADR 0429 §6). That reason now + says how the build stands, from the builder's `build.ended` the assigner already consumes and from the build it + waits on: a failed build reads `the build of at failed: `, the error whole as + `build.ended` carries it, `transport fault:` included (§568); a build that runs reads `the build of at + is being built`. The assigner says it in its wait for that module, which the watchman's + `delivery-waits` names; the node-engine reads no build state for it (ADR 0596). "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 (round 1 of this batch's review, seams and tools T1, placed the words in the assigner). How + it is checked: unit (assigner), a module whose build ended failed is held saying `failed:` with the error, and one + whose build runs saying `is being built`. ## The tool runner, and kept data at run time @@ -406,28 +486,46 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he `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. + acknowledges the event on its durable consumer of `mesh-delivery`'s events (§153). That consumer, when first + made, starts at the stream's first message. A `freed` said while it was stopped is applied when it comes back, + and one applied twice changes nothing. A `pinned` of a revision the indexer no longer holds writes its key with + the record's id and `missing: true`, and the finding `pin-missing` (scope the indexer, id the revision) naming + the record, which `indexer_unpin` clears. + - **The gate states its pins.** At each start the gate says `pinned` again for every revision each record it keeps + pins, then once `pins-stated` (`at`), so pins said before the indexer's consumer existed, or older than the + stream keeps, are heard again; the set form absorbs the repeats. - **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. + its key, and its entry objects as §179 has it, but only while its consumer of `mesh-delivery`'s events is caught + up (none of its messages pending), so a `pinned` said before is applied first, and only once it has applied a + `pins-stated` said after its state `pins` was made. It judges on applying `freed`, on a revision ceasing to be + current, on being caught up, and at its start after both of these hold, when it judges every kept revision, so a + stop between a take-in and a removal keeps nothing for ever. 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. + each by its own list alone. + - **The assigner** keeps its pins of assignment graph revisions in the same form and by the same rules: its old + `state` section `pins` (it is a foundation part, ADR 0619 §1), one key per assignment graph revision, capped at + 4 MiB; a revision is current while it is the newest the assigner wrote, or the newest it wrote for an open + candidate; its tools are `assigner_pins` and `assigner_unpin`, as the indexer's below. - **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`. + of class `read`. `indexer_unpin` (`revision`, `record`, `why`; class `act`, callers `operator` and `sessions`, + written in the hand-act log first) removes one record's id from a revision's list, for a `freed` that was lost, + and the removal rule then applies. 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. + `heads-replaced`; a revision that stops being current while a `pinned` of it is pending is kept; a `pinned` of a + removed revision writes `pin-missing`; `indexer_unpin` frees a revision; whole (indexer, gate), a `freed` said + while the indexer was stopped is applied after its start; a `pinned` said before the indexer of G first started is + heard through the gate's restatement; an indexer stopped between a take-in and a removal removes the revision at + its start. ## Observers with more than one bundle, and the forge module's forms @@ -446,7 +544,8 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - §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 + so the bus keeps no key time-to-live and no template option is added; its tool `git_seen_expire` (class `act`, + callers `operator` and `sessions`, written in the hand-act log) runs the same removal now. 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 @@ -477,13 +576,17 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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. + there. The operator, 2026-11-11 about 15:15 CET ("whatever must be done to not end up in a cycle"): at its first + start, before it serves anything, the image registry of G copies every key of its template `holds`, `windows` and + `mirrors` into its old sections, from its own data alone, and says in its journal and its read tool how many keys + it copied of each; a key already in the old section stays. This needs no other part's answer and adds no edge, + so it can close no cycle; holding its delivery until the template state is empty could wait for ever, since the + running registry keeps writing it. The template entries, no longer declared, then 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 + running registry's three bindings from `list`'s fields. 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 @@ -491,13 +594,16 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he 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 + refused; the catalog's validation at G's heads refuses neither; whole (image registry), a first start with keys in + its template `mirrors` copies them, says how many, and then serves them; the delivery plan of G puts the grants part's delivery before both builds. - §585 **What the forge module reads from `step-ended`, and the form of its `failed-steps`.** (§39, §475, §487, §581) The forge module's readings where no line spoke, made design lines: - **The fingerprint** of a step is the body's `fingerprint`, the build-input fingerprint, which the runner fills - in every `step-ended`. No field of the step's `result` is read for it, and a `step-ended` without it is written in - the forge module's journal and changes nothing. + in every `step-ended`. No field of the step's `result` is read for it. A `step-ended` without it writes the + forge module's finding `step-unread` (scope the module the step is of, id the run), naming the run, the module and + the step, cleared by a later `step-ended` of that step with its fingerprint, so a failure that could not be + recorded is seen. - **A failure** is a `step-ended` whose `outcome` is `failed` and whose `status` is `required`. An `optional` or `bypass` step's failure, and a `could not run`, record no failure, as ADR 0666 §11 refuses only a failed `required` step. @@ -508,16 +614,16 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he repository, read through `mesh-catalog`'s `registrations` and kept current on `registration-changed`. - **A `seen` key** `seen..` holds `{v, commit, module, fingerprints, at}`: `v` the form's version (1), `fingerprints` every build-input fingerprint the runs named for that commit and module, and `at` as §581 - has it. Both key forms of `failed-steps` are defined in the seat `git`'s contract, in the forge module's - contracts package. + has it. Both key forms of `failed-steps` are the forge module's own, defined in its own package, since no other + part reads that state. - **The fill** of `failed-steps` that its read tool shows is counted by the forge module from the keys and value bytes it reads, against the state's cap of 64 MiB, and said as counted, since a bundle's state call gives no fill of the bus's own. Why: the forge module's developer read these where no line spoke (develop, batch 5); each follows §39, §475, §487 and ADR 0666 §11. How it is checked: unit (forge module), a `required` failure recorded and an `optional` one not; - a rerun pass by `operator` clears and one by another caller does not; a `seen` value read in its form; unit (the - seat `git`'s contract), both key forms read. + a rerun pass by `operator` clears and one by another caller does not; a `seen` value read in its form, and both + key forms; a `step-ended` without `fingerprint` writes `step-unread`. - §586 **The client of `reserved` is the seat `mesh-builds`' contract package.** (§381, §478, §582) The forge module's package registry and the image registry module both ask `reserved`. The call is the seat `mesh-builds`' contract, so its client lives where ADR 0573 §1 puts a seat's contract: the package the seat's holder's diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index 2b952a31..852bc05b 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -65,7 +65,6 @@ decisions: - "ADR 0391" - "ADR 0363" - "ADR 0289" - - "ADR 0283" - "ADR 0253" - "ADR 0240" - "ADR 0283" -- 2.54.0