From 05b16d63bb1928cea2e695feae345010afe8c0e0 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 12:37:53 +0100 Subject: [PATCH 1/9] Answer develop's fourth batch of questions on milestone G, and write the operator's words for the teller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Develop met questions the design did not answer on restore points, a bundle's channel to the tool runner, the want's new fields, the finding stream's form under ADR 0595 and ADR 0666, and per-branch views; each is now a design line (G-12, §516 to §541) with its reason and check, so develop builds them and decides nothing. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../G-1-pipelines-registration-and-steps.md | 2 +- .../47-milestones/G-10-develop-answers.md | 8 +- .../G-11-develop-answers-review.md | 16 +- .../G-12-develop-answers-fourth.md | 364 ++++++++++++++++++ .../G-2-pipelines-sizes-checks-and-tools.md | 2 +- .../G-5-acting-parts-check-and-kept-data.md | 6 +- .../47-milestones/G-6-verbs-and-retires.md | 2 +- .../01-to-be/47-milestones/G-7-answers.md | 14 +- .../G-8-findings-heard-as-events.md | 6 +- .../47-milestones/G-9-prepare-answers.md | 14 +- 03-DESIGN/01-to-be/47-milestones/G.md | 3 +- 11 files changed, 401 insertions(+), 36 deletions(-) create mode 100644 03-DESIGN/01-to-be/47-milestones/G-12-develop-answers-fourth.md 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 89341c2d..5bb70ef3 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 @@ -267,7 +267,7 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l prerequisite step `bypass`) ends the pipeline refused within five minutes, naming the step and what it waits for, never as a silent wait (research 086, R5); a step whose seat has no holder ends its pipeline failed, naming the seat (ADR 0666 §17). -- §33 **A failed head is said to its author, then to the operator.** A `required` step that failed, or ended `could not +- §33 **A failed head is said to its author, then to the operator.** (§540) A `required` step that failed, or ended `could not run`, on a watched branch's head is the runner's finding `pipeline-failed`, naming the module, the branch, the commit and the step; the condition keeper raises it as the commit's author's for two hours and then the operator's, as ADR 0505 §5 and §6 have a delivery whose cure is its author's new head, and clears it when a newer revision of that branch diff --git a/03-DESIGN/01-to-be/47-milestones/G-10-develop-answers.md b/03-DESIGN/01-to-be/47-milestones/G-10-develop-answers.md index 95c73828..256f9aaf 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-10-develop-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-10-develop-answers.md @@ -42,7 +42,7 @@ each the design's choice with its reason, §468 to §479 those of the runner's, operator, 2026-11-11 at the terminal (develop question D-12): a refusal would be "hard-coded logic for no good reason". How it is checked: unit (definition reader), a row where a verb G adds with no `class` takes `read`'s callers; unit (seat generator), a definition with a verb naming no `class` is generated. -- §457 **The put session's wire form.** (§489, §490) §345's put session runs on the subject `.put.`. +- §457 **The put session's wire form.** (§489, §490, §518) §345's put session runs on the subject `.put.`. Each chunk is one message whose body is the value's raw bytes, at most half the bus's maximum payload as the connection reads it from the server, with the header `Mesh-Put-Seq` (a decimal from 1, one more each chunk) and, on the last chunk alone, `Mesh-Put-Last: true`. The tool runner answers each chunk with `{seq}`, the sequence it took, @@ -62,7 +62,7 @@ each the design's choice with its reason, §468 to §479 those of the runner's, its own version". Why: the operator, 2026-11-11 at the terminal (develop question D-19); a build without a version is §371's and §373's. How it is checked: whole (Go, TypeScript and image build modules), a built image's tag and label, a Go program's `main.meshVersion` and a Node program's own version are the reserved version. -- §459 **`SeatsToPassOn`'s input.** mesh-sdk's `catalogue.SeatsToPassOn` takes the holdings on record, the +- §459 **`SeatsToPassOn`'s input.** (§536) mesh-sdk's `catalogue.SeatsToPassOn` takes the holdings on record, the assignments (each a node, a module and its placement's watched branch), what the caller heard of each node and module from the node-engine seat's `running` (a fingerprint, `stopped`, or nothing heard since its consumer began), and a reader of entries the caller passes over its own view of the catalog half, which answers an entry by its @@ -77,7 +77,7 @@ each the design's choice with its reason, §468 to §479 those of the runner's, (ADR 0596 §2), and the function stays a pure rule (develop question D-14). How it is checked: unit (`SeatsToPassOn`), a table with a row per case: a heard fingerprint, nothing heard (the branch's entry taken), a `stopped` holder passing no seat, and an entry not answered keeping the holder on record and saying it. -- §460 **An asker says whether its key is still open.** mesh-sdk's kept-call form takes, beside the ask's key, a +- §460 **An asker says whether its key is still open.** (§531) mesh-sdk's kept-call form takes, beside the ask's key, a function `open` the asker passes, which reads the asker's own state and answers whether that key is still open, and, where one exists, the event that replaces the ask; it asks again only while `open` answers yes and no replacing event was heard. An error of `open` is written in the asker's journal naming the key, and the form asks again: a @@ -265,7 +265,7 @@ each the design's choice with its reason, §468 to §479 those of the runner's, its own fact (ADR 0596 §2) (develop questions GIT12 and REG1). How it is checked: unit (`mesh-builds` contract), `reserved` with `name` read; whole (builder, registry holder), a Go publish named by its module path is checked against the module's reservation, and an unknown name refused. -- §479 **A branch's catalog view is named with its branch.** (§495) In mesh-sdk's `go/views` reader and process reader, a +- §479 **A branch's catalog view is named with its branch.** (§495, §521, §532) In mesh-sdk's `go/views` reader and process reader, a first view of a per-branch state is named `..`, the branch escaped as a key's name is (§73), so `mesh-catalog.catalog-revisions.` names one view per watched branch, a part reads several in one reader, and `_first_views` lists one row per branch (§358, §365). A view of a state that is not per branch keeps diff --git a/03-DESIGN/01-to-be/47-milestones/G-11-develop-answers-review.md b/03-DESIGN/01-to-be/47-milestones/G-11-develop-answers-review.md index ecdda886..ea8162af 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-11-develop-answers-review.md +++ b/03-DESIGN/01-to-be/47-milestones/G-11-develop-answers-review.md @@ -128,13 +128,13 @@ reason. What a question of another kind leaves open waits for the operator and i not answer the new calls (ADR 0595 §9; review round 1, security and upgrade 2). How it is checked: unit (tool runner), each word stated with its family; whole (mesh-sdk), against a tool runner that does not state `put-sessions` a large put fails naming it; the delivery plan of G orders the modules after the tool runner. -- §489 **Who publishes on a put or get session.** On `.put.` only the module's own +- §489 **Who publishes on a put or get session.** (§518) On `.put.` only the module's own account publishes the chunks and only the tool runner of its node answers them; on `.get.` only the tool runner publishes the chunks and only the module's account answers. The grants part grants them so, as it grants a module's call subject today (ADR 0363 §2). Why: §457's subjects lie under a subject the module already owns, so the grant follows the record (review round 1, security and upgrade 3). How it is checked: whole (grants part, tool runner), a chunk published by another module's account is refused by the bus. -- §490 **The get side of a session.** A get of a value over the limit (§457) runs so: the bundle subscribes to +- §490 **The get side of a session.** (§518) A get of a value over the limit (§457) runs so: the bundle subscribes to `.get.` before it makes the call; the tool runner's answer to the call is `{chunked: , size, digest}`, the value's `sha256:` digest; the tool runner then publishes each chunk as a request on that subject, with `Mesh-Put-Seq` and, on the last, `Mesh-Put-Last: true`, and the bundle answers each @@ -170,7 +170,7 @@ reason. What a question of another kind leaves open waits for the operator and i release's publish (kind 1), so the runner's manifest is read by every part that takes it in. Why: readers first (ADR 0667 §4; review round 1, security and upgrade's note). How it is checked: G's check reads the runner's and the node-engine's manifests; whole (grants part), the runner's `pipelines` bucket has its cap. -- §495 **Small corrections from the review.** §472's "kind 3 is stale (§441)" reads §375. §472's `toolchain-agreed` carries its digests as they stood when the version was agreed; a digest that changes later at that version is read from `toolchain-reported` (§492) and starts nothing. A sent store already over its cap when §477's +- §495 **Small corrections from the review.** (§530) §472's "kind 3 is stale (§441)" reads §375. §472's `toolchain-agreed` carries its digests as they stood when the version was agreed; a digest that changes later at that version is read from `toolchain-reported` (§492) and starts nothing. A sent store already over its cap when §477's cap is planned keeps no cap until its seven-day age brings it under, the bus module's plan naming it with its fill meanwhile; a sent store is planned only for a module §344 names, and the plan refuses caps whose sum passes the bus's storage limit, naming the sum. §468's "behind" is the age of the oldest message the consumer has not @@ -197,7 +197,7 @@ reason. What a question of another kind leaves open waits for the operator and i `branch-unprotected` stands (§17) raises none either. Why: the watchman already times each take-in wait (develop question on §12). How it is checked: whole (watchman), a head behind a pending revision raises nothing for 60 minutes, then `take-in-waits`. -- §498 **A wait names its clock and its kind.** (§608) `meshwants.Wait`, the runner's wait body and the node-engine seat's +- §498 **A wait names its clock and its kind.** (§534, §608) `meshwants.Wait`, the runner's wait body and the node-engine seat's gain `clock-from` (the time the bound counts from, §266, §269) beside `bound`, and `step`, one of the acting steps of §203 or `put`, `delivery`, `kept-build`, `backup`; a put's wait (main file, *A put without hold that stands*) is `step: put` with the node, the module and `fingerprint`; a dependent's delivery wait (§376) is `step: delivery` @@ -205,7 +205,7 @@ reason. What a question of another kind leaves open waits for the operator and i fingerprint, `held`, since and bound, from which the watchman seeds. Why: one wait form, told apart by `step` (develop questions WM6 and WM10). How it is checked: unit (contracts), each field read; whole (watchman), a put's wait raises `put-stands` and a pipeline's wait raises nothing for it. -- §499 **The node-engine says a kept build's wait, and its version is read from the catalog.** (§609) A node-engine of G +- §499 **The node-engine says a kept build's wait, and its version is read from the catalog.** (§541, §609) A node-engine of G waiting for a kept build says `wait-held` on its seat with `step: kept-build`, `node` the build node, and why (`node away` or `build no longer kept`), and `wait-ended` when it applies; its have's value carries the same as `waits`. The watchman takes the keeping node from the builder's `build.ended` (§379), never its done key. Each @@ -252,7 +252,7 @@ reason. What a question of another kind leaves open waits for the operator and i never computed again from the manifest whole, whose encoding may differ; the module's value and §445's `manifests` key are that digest. Why: the bytes, not the map, are what the node-engines before G read (develop question IX16). How it is checked: unit (indexer), the entry's digest is the digest of the bytes read. -- §507 **The watched branches before the first registrations.** Every reader of a trunk's source revision reads, per +- §507 **The watched branches before the first registrations.** (§532) Every reader of a trunk's source revision reads, per watched branch the registrations name, `source.` through the gate's verb `source`; before the first registrations (§95, §353), the watched branches are the branches the gate's `source` answers, which G's switch carries in from `source.main` as the branch `main`, read from the key and never fixed in code (§7). The assigner's @@ -260,7 +260,7 @@ reason. What a question of another kind leaves open waits for the operator and i and one source of the branches for every reader (develop questions Q18 and VW0, and the bridge's FB9). How it is checked: whole (assigner, bridge), before the first registrations a want is written from `source.main`, and no part's code holds `main`. -- §508 **The node-engine claims its seat as one module.** (§609) The seat `node-engine`'s definition in mesh-contracts no +- §508 **The node-engine claims its seat as one module.** (§527, §609) The seat `node-engine`'s definition in mesh-contracts no longer marks it held by the engine; the manifest validator accepts one module of a revision claiming it and refuses a second, naming both, in place of refusing every claim, and §494's release carries the rule. The node-engine's guard reads the holder from the seat list (§364). Why: §364 has the node-engine's manifest claim the @@ -301,7 +301,7 @@ reason. What a question of another kind leaves open waits for the operator and i `membership.BusBinding`, built in mesh-sdk. Why: the objects template has no default (develop questions NATS1 and on §399). How it is checked: unit (bus module's plan), each planned objects store has its cap; unit (membership), both fields read. -- §514 **The teller tells through `notify`, on every channel.** For `widens` (§71, §335), `recovery-merged` (§81) +- §514 **The teller tells through `notify`, on every channel.** (§522) For `widens` (§71, §335), `recovery-merged` (§81) and a `forge-made` record (§170) the teller calls the seat `operator-channel`'s verb `notify`, named in its manifest's `invokes`, with `every-channel: true`, which the router sends on every channel's `send`, as design 46 announces a break-glass act. The words are the operator's to approve and wait for them (develop questions TL1 to 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 new file mode 100644 index 00000000..0ad1d984 --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-12-develop-answers-fourth.md @@ -0,0 +1,364 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-11 +decisions: + - "ADR 0667" + - "ADR 0666" + - "ADR 0660" + - "ADR 0656" + - "ADR 0636" + - "ADR 0619" + - "ADR 0618" + - "ADR 0614" + - "ADR 0596" + - "ADR 0595" + - "ADR 0564" + - "ADR 0505" + - "ADR 0363" + - "ADR 0240" + - "ADR 0235" +--- + +# 47, milestone G: the operator's words of 2026-11-11 and develop's fourth batch of questions + +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". + +*§522 is the operator's answer of 2026-11-11 at 12:11 CET at lead-47's terminal. Every other line answers a question +develop met after [G-11](G-11-develop-answers-review.md), of internal form only (playbook 07, step 0b), each the +design's choice with its reason. What a question of another kind leaves open waits for the operator and is said in the +line. Questions that an earlier line already answers have no line here: develop reads that line.* + +## Kept data on the bus: restore points and what is set aside + +- §516 **A restore point's bytes, and the forms of `snapshot-kept` and `restore-kept`.** The seat `mesh-broker`'s two + verbs (§405) take and answer the forms its definition in mesh-contracts gives them, which this line makes the + design's: `snapshot-kept` takes `module`, `kept` (the kept data's local name), `form`, `fingerprint`, `why` and + `catch-up` as `{tool, arguments}` (§451), and answers `point` (the module, the kept data, its form, the + fingerprint, `snapshot`, `consumers` with each durable consumer's stream, name and acknowledged sequence, `size` in + bytes, `at` and `catch-up`) or `error` alone; `restore-kept` takes `module`, `kept`, `form`, `fingerprint`, `why` + and one of `snapshot` (the restore point's name), `aside-only` or `discard-aside` (an aside's `id`), and answers + `restored`, `aside` and `error`. No argument `local-name` or `from` is taken, and a catch-up is never text. The bus + module keeps a restore point in its own `snapshots` directory, which ADR 0235 §1 to §3 give it for the bus's dump: + the server's snapshot of each bus object of the kept data (its stream, or the stream under its bucket or object + store), taken one at a time with ADR 0235 §10's pacing and §11's bounds, and the point's record, in the directory + `kept///
-`, each token escaped as a key's name is (§73); that name is the point's + `snapshot`. The holder of `node-backup` backs that directory up as it backs up `snapshots` (ADR 0235 §3), so its + copy is the point's safeguard, never the point: when its `restore` finds the point's directory missing on the bus + module's node, it brings it back from its newest repository snapshot that holds it, under the same name, and only + then calls `restore-kept`; when none holds it, `restored` says `failed` with `restore point missing`, and §300 holds + the revert. `restore-kept` sets the live kept data aside by taking the server's snapshot of each of its bus objects + into `aside///` in the same directory (`at` the UTC time written `20261111T121200Z`), only then + removes the live objects, restores the point under their names and sets each consumer to its recorded sequence. The + aside's `id` is that directory's name, its `name` the bus name it was live under, its `size` its bytes on disk; + nothing of an aside stays on the bus, so it takes none of the bus's storage limit, and the definition's + description of `name` reads "the bus name it was live under". `discard-aside` removes that directory and nothing + else. No part removes a restore point in G: how long one is kept waits for the operator (issue 1921's note), and + until the answer is written here every point stays. This completes §405 and §406. Why: ADR 0235 has the bus + module's account write the bus's snapshots into its own directory and the backup holder back that directory up; + the server restores a stream only where no stream of that name exists, so the live data is set aside as a + snapshot before the point takes its name (develop questions BKP1, BKP3 and NATS4's argument names). How it is + checked: unit (mesh-broker's contract), each verb's input and answer read, an input naming `from` refused; whole + (bus module, backup holder), a point is taken, its directory removed, brought back from the repository and + restored, the live data's aside listed with its size and the consumer at its recorded sequence; a point missing + from both says `restore point missing`. +- §517 **`backed-up` reads the restore points and the data set aside from the backup holder's own state.** The holder of + `node-backup` on the bus module's node keeps what `snapshot-kept` and `restore-kept` answered it in its state + `bus-points`, asked in its `requires` by the template `state` (ADR 0595 §1; the holder is no foundation part, ADR + 0619 §1, ADR 0666 §4): a point under `point....` with the answer's `point` whole, + written when `snapshot-kept` answers done; an aside under `aside...` with the answer's `aside` + whole, written when `restore-kept` answers one, each token escaped as a key's name is (§73). The holder deletes an + aside's key when `discard-aside` answers done, and deletes no other key in G (§516). `backed-up` lists both from + that state, each with its size (§302), and the holder's read tool of its state reads it. The seat `mesh-broker` + gains no read verb. The holder's old `state` entry `good` moves to `requires` in the same change, keeping its local + name and settings, as ADR 0595 §11 moves a module G touches. Why: the holder took each from its own call's answer, + so the list is its own fact (ADR 0596 §2), and a read verb on `mesh-broker` would answer what the caller already + holds (develop question BKP2). How it is checked: whole (backup holder), a restore's aside is listed by `backed-up` + with its size, is gone after `discard-aside`, and a restart of the holder lists the same. +- §523 **A seat event whose subject has parts.** A seat definition's `subject` with `` tokens (§397), such as + `node-backup`'s `ended...` and `restored...` (§405), 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. A reader hears one module's events by the subject with + that module's tokens and `>` after them, and every module's by `*` for each part. Such an event has no key of its + own and is kept as its definition's `keep` says, or on EVENTS when it names no `keep` (§396). The holder's account + publishes it as a seat's holder publishes its seat's events today; no grant changes. Why: §397 fixes the subject's + words, not how a part is filled (develop question NATS4's wire). How it is checked: unit (seat generator), a subject + of three parts made in order and an empty part refused; whole (backup holder), `ended` heard by a reader of one + module's subject. + +## The tool runner and its bundles + +- §518 **A bundle's put and get sessions run on its channel to the tool runner.** A tools bundle reaches the tool runner + only over the channel the tool runner starts it with (its standard input and output) and holds no bus account, so + §345's put session and §490's get session run on that channel, as every `mesh/` call of a bundle does (ADR 0595 + §9); `` in §457, §489 and §490 names that call, and no bus subject. A value at or under the + limit is put and got by one call, as today. Over it: + - **Put.** The bundle sends each chunk as the call `mesh/objects.put-chunk` with `name` (the object), `call` (the + bundle's id for this put), `seq` (from 1, one more each chunk), `last` (true on the last chunk alone) and `data` + (the chunk's bytes in base64, at most half the bus's maximum payload before encoding, §457). The tool runner + answers each with `{seq}` or `{error}`, the bundle sends the next only after a `{seq}`, and the answer to the last + is the put's answer; a `seq` that is not one more than the last taken is answered `{error}` naming both, and a put + not sealed within the call's bound is dropped (§345). The tool runner writes the whole as one object through the + object store's own chunking. + - **Get.** The tool runner answers the get call `{chunked: , size, digest, call}` (§490), and the bundle asks + each chunk in order by the call `mesh/objects.get-chunk` with `call` and `seq`, answered `{seq, last, data}` or + `{error}`; the bundle checks the whole against `digest`, and a get not completed within the call's bound fails, + naming the object. + + The headers `Mesh-Put-Seq` and `Mesh-Put-Last` of §457 are these fields `seq` and `last`. Nothing of a session is + on the bus, so §489's grants are not made and the grants part grants nothing for it. A module's process, which + holds a bus account of its own, writes and reads its objects on the bus through the object store package directly, + with the store's own chunking, and runs no session. The capability word `put-sessions` (§488) names these two + calls. This amends §345, §457, §489 and §490; in the get the bundle asks each chunk, since on that channel every + `mesh/` call is the bundle's. Why: the operator's answer to develop question D-13, chunks in order, each + acknowledged before the next, is kept whole on the one channel a bundle has (develop questions T-5 and T-7). How it + is checked: whole (mesh-sdk, tool runner), a value of a hundred times the limit is put and got back whole by a + bundle, its digest checked, and a chunk out of order fails the put naming both sequences; the grants part's table + test grants no session subject. +- §519 **A bundle's fingerprint is in the environment the node-engine composes.** The node-engine writes + `MESH_FINGERPRINT`, the fingerprint of the want it applies for the bundle's module, into each tools bundle's + composed environment, as it hands it to each process (§205); the tool runner starts each bundle with its composed + environment whole, and reads no catalog entry and no want for it. A bundle started with no value says `own build + unknown` (§424). This completes §424. Why: the want the node-engine applies is the one fact of which build a node + runs (§205; develop question T-12). How it is checked: whole (node-engine, tool runner), a bundle reads the + fingerprint of its module's want. + +## The node-engine and the want + +- §520 **`running` says a build's health, and the assigner reads health from it alone.** The node-engine seat's + `running..` gains `healthy` (true or false), the health of the build that runs as the node-engine's + have holds it from the module's declared check (ADR 0240), and the node-engine says `running` again on the same + subject each time `healthy` changes, so the stream, kept last per subject, holds each build's newest health. + `stopped` (§365, §404) is said only when the have no longer holds the module, never for an unhealthy build. The + assigner reads a provider's health for rule 5, the canary's for rules 3 and 4, and a put's end from these events + alone, through its durable consumer that starts from the last message per subject (§199), and reads no have: a + provider of which nothing was heard since that consumer began counts as not healthy, so its consumers' new wants + are held as rule 5 holds any, and nothing falls back to the have. `SeatsToPassOn` reads no `healthy` (§459): an + unhealthy holder still holds its seat. The assigner's `reads` of the node-engine's have go. This amends §309 and + §404. Why: the assigner reads no other module's state (ADR 0596 §2), and a stopped build and an unhealthy one are + told apart (develop questions AS15 and AS16). How it is checked: unit (the node-engine seat's fixtures), `running` + with `healthy` read; whole (assigner, node-engine), a provider turning unhealthy holds a consumer's new want and its + recovery lets the want go, with the assigner's account holding no read of the have. +- §524 **The node-engine checks a want's parts by their digests.** A composition's archive, process and container + resources name a source and a digest, not an artifact, so the node-engine matches no resource to a `parts` key: it + applies a want 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 + 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. +- §525 **A node-engine says it reads the want's new fields.** A node-engine of G states the capability `want-parts` in + its node record's capabilities (ADR 0614 §4, ADR 0618 §2), and the assigner writes `version`, `parts` and `kept-on` + (§526) into a node's want only while that node's record states it; that is §379's "once that node runs G's + node-engine". Why: ADR 0614 §4 has each node-engine state what it reads (develop question H-7). How it is checked: + unit (node-engine), the word stated; whole (assigner), a node whose record lacks it gets a want without the fields. +- §526 **A want names the node that keeps a kept build.** For a kept build (§271) the assigner writes `kept-on`, the + build node the builder's `build.ended` names as keeping it (§379), into the want beside `parts`; the node-engine + fetches that build through the seat verb `kept-artifact` of that node, and fetches a want with no `kept-on` from the + store. Nothing else names the keeping node, the builder's `builds` going (§421). This amends §271's "the builder's + done key names it so with its digest". Why: ADR 0596 §2 (develop question H-9). How it is checked: whole (assigner, + node-engine), a kept build's want names its node and is fetched from it. +- §527 **The node-engine's own module is read from the catalog half.** The seat list answers seat definitions alone + (ADR 0636), never who holds a seat. The node-engine's apply guard and its compose read their own module's name from + the catalog half of their view: the module whose entry claims the seat `node-engine`, which §508 makes one per + revision; the manifest validator, beside §508's refusal of a second claim, refuses a revision in which no module + claims it, naming the seat. §364's, §365's and §508's "from the seat list" read so, the condition keeper's too. With + no entry claiming the seat, which only a revision from before G can show, the node-engine applies nothing of that + revision and its have says `entry-missing`, naming the seat. Why: §364 and §508 have the node-engine claim its seat + as one module, so the claim names it (develop question H-15). How it is checked: unit (manifest validator), a + revision with no claim of `node-engine` refused; whole (node-engine), its guard protects the module its catalog + half names, and the code review finds no fixed name (§428). + +## Findings and the condition keeper + +- §529 **A part's finding state and stream are declared in its kept data's form.** A part's state `findings` and its + finding stream (§383) are declared as ADR 0666 §4 has a part declare its kept data: + - **A foundation part** (the manifest validator's list: ADR 0619 §1's parts as ADR 0666 §4 changes them, the runner + from G's delivery and the checker until its removal) declares `findings` in its old `state` section and the + stream `-findings` in its old `streams` section, with the events `finding-written.*`, `finding-cleared.*` + and `findings-stated.*`, 7 days and 16 MiB, its oldest dropped (§383), published on + `mesh.stream..event..` (ADR 0363 §2). + - **Every other part** asks both in its `requires`: the state `findings` by the template `state`, keeping its local + name and carrying its settings as options (ADR 0595 §11), and the stream by the template `events` under the local + name `finding-events`, with `max-age-days` 7 and `max-mib` 16, which the template drops oldest when full (ADR 0595 + §3), published on its binding's subject, `mesh.bus..finding-events..`. `mesh-healers`, whose + old `streams` declares `mesh-healers-findings` on main, moves it so in G's change. + + The manifest validator applies ADR 0595 §11's count of old sections to no part of its foundation list (ADR 0619 + §2), so the tool runner's and the checker's manifests are not refused for it, and accepts in a foundation part's + old `streams` the finding stream in the one form above, as it accepts a stream on its list of streams kept until + moved; every other old `streams` entry is judged as today. A part whose list entry is missing is a fault of the + list, fixed in the validator (ADR 0619 §3). The validator refuses a manifest whose state `findings` has no finding + stream in its part's form, or no `consumes` of `mesh-conditions.findings-asked` (§387). The writer (§461) is handed + the subjects from the part's binding or its old stream, as it is handed every subject. The catalog graph entry + carries the finding stream in either form (§365); the grants part makes the keeper's consumer of §386 on either + stream and grants each part publish on its own finding stream's subjects of its own node only, in either form; the + keeper takes the observer and the node from either subject. This amends §383, §386 and §387, whose "into the + manifest's `streams`" holds for a foundation part alone. Why: ADR 0666 §4 and ADR 0595 §11 put a part's kept data + in the templates unless it is a foundation part, and ADR 0619 §2 keeps the foundation's old sections whole; the + local name `finding-events` is the design's choice, since `findings` names the state and ADR 0595 §8 takes one + name per module (develop questions on §387, both validator refusals). How it is checked: unit (manifest validator), + a foundation part's old `findings` and finding stream accepted, a non-foundation part's old sections refused and its + template form accepted; the catalog's validation at G's heads refuses none of §387's manifests; whole (keeper, a + template observer and the tool runner), both forms' events raise a condition. +- §540 **The keeper hands a failed head to the operator after two hours.** The condition keeper raises + `pipeline-failed` (§33) with `owner` the commit's author, as the runner's finding names it, and two hours after the + finding's `since` sets the condition's `owner` to `operator-channel`, the seat the operator is asked through (a + seat's name, §451), and says that change as it says any change of a condition; the runner's finding keeps the + author. The keeper times the two hours on its own conditions from `since`, so after its restart it times them from + `since` again. Why: §33 has the keeper raise it as the author's, then the operator's (ADR 0505 §5, §6; develop + question COND7). How it is checked: whole (keeper), a `pipeline-failed` is the operator's two hours after its + `since`, and so after a restart of the keeper. +- §541 **The keeper hears what a node runs and the healers' attempts as events.** The keeper's `reads` of + `node-engine.have` and `mesh-healers.attempts` go (§434). + - **What a node runs.** The keeper consumes the node-engine seat's `composed`, `running` (with `healthy`, §520, and + `stopped`) and `wait-held` and `wait-ended` (§499), from the last message per subject (§199), and compares them + with the wants it hears from the assigner. The node-engine says `wait-held`, with `step: compose` and the reason, + for each hold its have writes (§300's `restore point missing`, §421's and §527's `entry-missing`, §524's + `parts-differ`, §499's kept build), and `wait-ended` when the hold ends. + - **The healers' attempts.** The seat `mesh-healers` says `attempt-changed` on + `mesh.seat.mesh-healers.event.attempt-changed.`, the condition's key escaped as a key's name is (§73), + carrying the attempt's value whole as its key in `attempts` holds it and `until`, the end of that key's + lifetime, or `removed: true` when the healer ends the attempt; the seat's definition, in its contracts module, + keeps it last per subject, 7 days and 16 MiB (§396). The keeper's durable consumer starts from the last message per + subject, and the keeper drops an attempt at its `until` on its own clock. + + Why: the keeper reads no other module's state (ADR 0596 §2; develop question COND8). How it is checked: whole + (keeper, healers, node-engine), an attempt said is shown on its condition and dropped at its `until` or its + removal, a node-engine's hold is heard from `wait-held`, and the keeper's manifest declares no `reads`. + +## Views of the catalog half, per watched branch + +- §521 **An owner's view event names its state, and the key names the branch.** An event an owner says for a per-branch + state (`catalog-revision` for `catalog-revisions`) names the state as the owner's manifest names it, + `catalog-revisions`, and the key, `.` (§343); mesh-sdk's `go/views` reader and process reader + route it to the view `..` (§479) by the key's first token, the branch escaped as a key's name + is (§73), which the subject's last token repeats. No event names a state `.`; mesh-sdk's code at + eed96854, which names it so, follows this line. Why: a state is named once, as its owner declares it, and the key + already carries the branch (develop question on §479 and §495). How it is checked: unit (`go/views`), one + subscription of a per-branch event routes two branches' events to their two views. +- §530 **`revision` says `none yet` in a field.** The seat `mesh-catalog`'s `revision` for a watched branch with no + revision yet answers `{none-yet: true}`, with no revision and no `error`; mesh-sdk's client reads `none-yet` as a + ready, empty view (§495) and never reads an error's text for it; `views.NoneYet` is the client's name for that + answer. Why: an answer is told by its fields, never its words (develop question on §495). How it is checked: unit + (mesh-catalog's contract), the answer read; unit (client), `none-yet` gives a ready, empty view. +- §532 **Every reader of the catalog half follows every watched branch, and adopts a branch registered while it runs.** + Every part that reads the catalog half per watched branch (among them the bus module, the status view, the + condition keeper, the tool runner, the assigner, the grants part and the runner) follows one view per watched + branch: the branches the registrations name, read by `registrations` at its start and followed by + `registration-changed`, and before the first registrations the branches the gate's `source` answers (§507); no part + names a branch in its code, the view's `main` included. + - **Adopted after the start.** mesh-sdk's views reader adopts a view after its start and drops one: on + `registration-changed` adding a watched branch the part adopts `..`, which takes its first + view at once and then follows; on one removing a branch the part drops the view and its row of + `_first_views`. + - **One subscription per event.** The reader subscribes once per event, `.>` + (`mesh.seat.mesh-catalog.event.catalog-revision.>`), and routes each event to its branch's view by §521, so two + branches never overwrite each other's handler; `meshcatalogclient` wires its views on that one reader, and the + runner's own wiring of `catalog-revision.>` goes for it. + - **The bus module across branches.** The bus module plans a module's kept data from the newest revision of each + watched branch that lists the module: each local name once, from the first of the module's watched branches, in + its registration's order, whose revision lists it; another branch asking that local name with other settings is + said as the bus module's `drifted`, naming the branch (ADR 0595 §6), and plans nothing. + + This amends §358, §365, §421, §433 and §507. Why: no part fixes a branch (§7, ADR 0666 §5), and a registration may + add a watched branch while a part runs (develop questions NATS3, VW1 and COND6, and the client's lost per-branch + events). How it is checked: unit (`go/views`), a view adopted after the start takes its first view and follows, + and two branches' events reach their own views; whole (keeper, bus module), a branch registered while they run is + followed with no restart; the code review finds no branch's name in any part. + +## The bus module's plans + +- §533 **A module of the new form.** "A module of the new form" (§59, §344) is a module that is no foundation part. + The bus module tells it by its catalog graph entry declaring no old `objects` section `sent-objects`: a foundation + part whose entry claims a seat whose definition has a verb or an event, or declares an event of its own, declares + it (§59), and the manifest validator refuses such a foundation part's manifest without it, naming it. Why: the bus + module reads no list of foundation parts, which is the validator's alone (ADR 0619 §3; develop question on §59 and + §344). How it is checked: unit (manifest validator), a foundation part saying an event without `sent-objects` + refused; whole (bus module), a module of the new form gets a sent store and a foundation part none. +- §537 **A definition's included parts are resolved before they are counted.** The bus module counts a seat + definition's verbs and events after resolving the parts it includes: it reads each definition that includes parts + by the seat list's answer for that one seat, which resolves them (ADR 0636), and plans a sent store (§344) for the + seat's holders when the resolved definition has a verb or an event. Including parts alone counts for nothing. + Why: an included part may hold only shared bodies (develop question on §344). How it is checked: whole (bus module), + a seat whose verbs come only from an included part gives its holder a sent store, and one including only bodies + gives none. +- §538 **The bus module plans again when the bus-state entries change.** The bus module compares, for each module, + the bus-state entries the catalog graph entry carries with those it last planned for that module, kept in its own + state, and plans again only when they differ; it never compares the manifest's digest (§506), which changes with + any field. Why: what it plans is those entries alone (develop question on §433 with §506). How it is checked: whole + (bus module), a manifest changed outside `requires` plans nothing, and a changed option plans the difference. + +## The runner, the checker and mesh-sdk's helpers + +- §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 + 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. +- §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, + `runner_waits`); `clock-from` stands beside it (§498). The watchman tells them apart by the value: `pipeline` and + every value §498 lists are acting or delivery waits, timed by their bound (§269); a pipeline step's value is a + step's wait on a person, a build node or the revision view (§73), timed as §75 has it, a wait on a person owing no + progress (ADR 0666 §24). This completes §498 for the runner. Why: one field, told apart by its value (develop + question on §498). How it is checked: unit (runner's contract), both kinds read; whole (watchman), a runner's + `pipeline` wait raises `acting-part-late` past its 30 minutes and an `approval` wait raises no `pipeline-stands`. +- §535 **A missing build seat and a seat with no holder, each read where it shows.** The runner reads that no build + seat serves an artifact's language from the catalog half of its revision view: no entry whose `acts`, the acting + rules of the seats it claims, hold a rule `build` with `on` naming that language (§323); the pipeline then ends + failed at once, naming the language and the missing build seat. It reads that a seat has no holder from the step + itself: an ask on that seat's work queue whose `step-started` is not heard within five minutes ends the pipeline + failed, naming the seat (§32). It reads neither from the seat list, which answers definitions alone (§527), nor + from any holding. Why: §32 already ends a pipeline whose step nothing starts, and the catalog half is the runner's + one view (develop question Q-RN7). How it is checked: whole (runner), a fixture language with no build seat ends at + once, naming it; a seat whose holder is stopped ends within five minutes, naming the seat. +- §539 **The runner's tools are named `runner_`.** The runner's module, `mesh-delivery-pipeline`, names its tools with + the prefix `runner_`, so the design's `runner_catch_up`, `runner_waits`, `runner_release` and `runner_health` + stand, and its first-view tool is `runner_first_views`; `_first_views` (§205) carries the prefix the + module's tools carry. Why: the design names the runner's tools so throughout (develop question RN1). How it is + checked: the runner's manifest, and `mesh_search` with "runner first views" finds `runner_first_views`. +- §531 **The kept-call form in Go.** §460's form is mesh-sdk's `busretry.DoKept`, which takes the ask's key, `open` (of + the type `busretry.Open`), an optional `replaced` and the function that writes the journal, and a process's call + through it is `process.Conn.CallKeptWhileOpen`, as mesh-sdk 19fb1dce has them. Why: a helper's API is the design's + (develop question R-5). How it is checked: §460's. +- §536 **`SeatsToPassOn`'s types.** §459's function in mesh-sdk's `catalogue` takes `holdings`, `assignments` (a list + of `Assignment` with `Node`, `Module` and `Branch`), `heard` (a function of a node and a module answering a `Heard` + with `Fingerprint` and `Stopped`, and whether anything was heard) and `entries` (an `EntryReader` with + `ByFingerprint` and `OnBranch`), as mesh-sdk e0f9356b has them. Why: a helper's API is the design's (develop + question R-4). How it is checked: §459's. + +## The operator's words + +- §522 **The words the teller says.** The teller says, through `notify` on every channel (§514), in the operator's + words: + - `widens`, one line per entry of the pipeline's end (§335): "Widening: `` `` on `` widens + `` ``.", `` and `` the entry's. + - `recovery-merged` (§81): "Recovery merged: `` `` on ``, named by ``: ``." + - a `forge-made` record (§170): "Merged at the forge, not through the mesh: `` pull request #`` at + `` on ``; delivered like any merge." + + `` is the commit's first seven hexadecimal characters. This completes §514. Why: the operator, 2026-11-11 + 12:11 CET at lead-47's terminal, approved the words as the design writer proposed them. How it is checked: unit + (teller), each of the three said in these words with its fields filled; whole (teller, router), as §514. + +## Questions an earlier line answers + +These write nothing new; develop reads the line named. + +- The tool runner's fallback of local commit 1015b38 for a foundation part's bundle goes: §407 binds that bundle to + its old `objects` section, and a binding that is missing fails loudly (ADR 0595 §1) (Q-T5, T-11). +- The re-read tool's callers of `seatlist.Part` are §204's for every part, the assigner's own list going (AS19). +- The bus module's cap of a state's objects store and the binding's `objects` and `sent-objects` are §513's (NATS1, + §399). +- The contracts' `Hold` is §194's view, the newest assignment half and the catalog half it names; the candidate's + whole-half reader is §195's and §441's, read by a run on request's steps, `grants-for` and the indexer's candidate + pass (contracts). +- The view's module value carries the entry's `manifest-digest` (§506) and computes none (VW1). +- `store-filling` is raised past 80 % and cleared below 70 % (§181). diff --git a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md index dc3ebc13..c8f6b8aa 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md +++ b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md @@ -47,7 +47,7 @@ One file of milestone G's part of [design 47](../47-delivery-from-commit-to-deli Its size is the template's (ADR 0595 §3). An object store holds no space until a value is written to it, so the many it adds reserve nothing; the few values over the limit (a delivery record, a catalog graph revision list, a test's output) are estimated, as a guess, at under 1 GiB across the mesh. -- §59 **An object sent is readable by the modules it is for.** A reply's or an event's object is written to the sender's +- §59 **An object sent is readable by the modules it is for.** (§533) A reply's or an event's object is written to the sender's **sent store**, `_sent-objects`: the bus module plans it, with no manifest field, for every module of the new form that serves a verb or emits an event, and a foundation part declares it as an old `objects` section `sent-objects` (ADR 0619 §2). Its cap is the objects template's (ADR 0595 §3), 256 MiB (§477), and it keeps an object 7 days, after diff --git a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md index 7f899b12..88526d31 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md +++ b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md @@ -97,7 +97,7 @@ names go, since every new acting part needed code changes elsewhere (research 08 what its new version is built from or needs (a base, a package, a provider) is acted on by its running version, never waited for: the running build module builds the shared contracts and the registry holder, then the new build module builds the rest, so ADR 0614 §6's cycle through them stays allowed, now for that reason. -- §271 **The artifact store's holder takes its own new build even when its seat is the one broken.** (§499) A build module +- §271 **The artifact store's holder takes its own new build even when its seat is the one broken.** (§499, §526) A build module stores the artifacts it builds through the seat `oci-artifact-registry`, so the holder of that seat is in its own build's path: its new build needs the store it replaces. As every holder's own inputs are (above), that store is acted on by the running holder: the running holder stores its new build, and only @@ -381,7 +381,7 @@ later change of a kept data's form, not as a case of this milestone. ask the gate of milestone G raised after the restore point is not among the pending ones, so its answer is not read and the operator is asked again. Whether the first look clears a merge kept in the restored `merges` that the forge merged since was not read in the code; the catch-up test below proves it with the real builds. -- §300 **A missing restore point holds the revert, naming it.** When the restore point a revert needs does not exist, or +- §300 **A missing restore point holds the revert, naming it.** (§541) When the restore point a revert needs does not exist, or `restored` does not come, the node agent applies nothing of that module, keeps what runs, and writes the have phase `held` with the reason `restore point missing` and the module, kept data and form it names; the assigner asks the operator (hold, or start the reverted build on the live data as it is), raises the condition naming them, and on @@ -391,7 +391,7 @@ later change of a kept data's form, not as a case of this milestone. (for milestone G, its delivery records and its graph revisions) is set aside and not read again; an Approve asked and answered since is asked again, and one the gate asked before still reads its answer. The revert's Approve ask says this in those words, naming each kept data restored. -- §302 **Its tools.** The seat `node-backup`'s `backed-up` lists each module's restore points of its bus data, with the +- §302 **Its tools.** (§517) The seat `node-backup`'s `backed-up` lists each module's restore points of its bus data, with the kept data, the form and the fingerprint each was taken before, and the data set aside at each restore, with its size; `restore` restores one; `discard-aside` removes data set aside by a restore, only on the operator's call, and writes the act, who and why in the hand-act log of the seat `mesh-hand-acts`. `backed-up` and `restore` exist on diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md index aa43ef48..487f2e5a 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md @@ -75,7 +75,7 @@ the seat `git` the verb `tag` (§349, §372, §375, §376, §370). of the revision view, ADR 0656), and `entry` and `difference` on `mesh-wants` (ADR 0656), with the events of *What each step says*. -§309 **The seat `node-engine`**'s event `running..` carries, from research 085's milestone, the module, its +§309 **The seat `node-engine`**'s (§520) event `running..` carries, from research 085's milestone, the module, its fingerprint and the digest of its build's manifest, so the node-engine of another node and the grants part decide a seat's pass from the catalog graph's entry of that build (ADR 0660 §2; *Who holds a seat is applied by one rule*). No verb is added. diff --git a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md index 5cf04515..37b8d268 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -43,7 +43,7 @@ record it follows from, and replaces the builder's own choice where one was made ### The runner and its steps -- §323 **A step's build seat is read from the artifact's language.** The catalog graph entry names each artifact's +- §323 **A step's build seat is read from the artifact's language.** (§535) The catalog graph entry names each artifact's `language`, as its manifest's artifact entry says it and as the indexer's language map matches today; the runner reads it from the catalog half of its revision view and asks lint and the test steps of the build seat whose definition's acting rule `build` has `on` naming that language. With no such seat, or no holder of it, the pipeline @@ -198,14 +198,14 @@ record it follows from, and replaces the builder's own choice where one was made - §343 **Branch names the indexer's keys use.** `register` and `watch` refuse a watched branch named `candidate` or `entry`, naming why, since the catalog keeps its revisions under `.` beside those two key forms. How it is checked: whole (indexer), each is refused. -- §344 **The bus module plans a sent store from what the catalog already says.** It plans `_sent-objects` for a +- §344 **The bus module plans a sent store from what the catalog already says.** (§533, §537) It plans `_sent-objects` for a module of the new form whose catalog graph entry claims a seat whose definition has a verb or an event, or declares an event of its own, read from the catalog half of its revision view and the seat list; the indexer's event carries no field `sends`. When a seat's definition gains its first verb or event later, the bus module plans the store for its holders on the seat list's change. Why: no manifest field (*Any size*), and the catalog half is every part's view (ADR 0656 §6). How it is checked: whole (bus module), a module claiming a seat with a verb gets a sent store, one with neither gets none, and a definition gaining a verb gives its holder one. -- §345 **A large value reaches the tool runner in chunks.** A bundle's put of a value over the bus's per-message limit is +- §345 **A large value reaches the tool runner in chunks.** (§518) A bundle's put of a value over the bus's per-message limit is sent to the tool runner as a put session on the bundle's call subject, named by the call's id, in chunks under that limit, in order, each answered by the tool runner before the next is sent, and written as one object through the object store's own chunking, its wire form §457's; a put not sealed within the call's bound is dropped by the tool runner and its call @@ -279,7 +279,7 @@ record it follows from, and replaces the builder's own choice where one was made candidates, and nothing is guessed. A contribution edge runs from a consumer's entry to the provider its requirement resolves to, wherever that requirement carries values the consumer contributes. How it is checked: unit (assignment graph), each step of the order and a hold. -- §358 **The catalog's shared view takes a branch.** The client's view of the catalog half is per watched branch, named by +- §358 **The catalog's shared view takes a branch.** (§532) The client's view of the catalog half is per watched branch, named by its caller (§479). How it is checked: unit (client), two branches give two views. - §359 **`difference` names its revisions.** `difference` with none of `from`, `to` and `group` is refused, naming the missing argument. How it is checked: a unit row. @@ -299,10 +299,10 @@ record it follows from, and replaces the builder's own choice where one was made ### The condition keeper and the node-engine -- §364 **The node-engine claims its seat.** (§508) The node-engine's manifest claims the node seat `node-engine` (ADR 0563), and +- §364 **The node-engine claims its seat.** (§508, §527) The node-engine's manifest claims the node seat `node-engine` (ADR 0563), and the indexer and the node-engine's own guard read its holder from the seat list, keeping no module name. How it is checked: the code review reads that neither fixes a module name (§7). -- §365 **The condition keeper finds its sources in the catalog.** The catalog graph entry carries the events each +- §365 **The condition keeper finds its sources in the catalog.** (§527, §532) The catalog graph entry carries the events each manifest declares in its `streams`; the keeper takes a first view of the catalog half, and its sources are the modules whose entry declares the finding events of §383, the node-engine among them, its holder read from the seat list. It joins the readers of *Every other reader of which modules exist*, reading every watched branch's newest @@ -486,7 +486,7 @@ record it follows from, and replaces the builder's own choice where one was made dependency builds it (its build toolchain part). Every other kind (2, 4, 5 and 6) only orders. A change to a build module's own code, which is no toolchain part, rebuilds nothing. Why: the operator, 2026-11-10 about 22:30 to 23:00 CET. How it is checked: unit (indexer's graph), each kind's stale-or-order, and a build module's code change rebuilds no module. -- §379 **A want names the released version and each part's digest.** From G's delivery a want `want..` +- §379 **A want names the released version and each part's digest.** (§524, §525, §526) From G's delivery a want `want..` names, beside its fingerprint and source revision, the module's released version, for people (none for a build of another watched branch), and the digest of each of its parts; the node-engine fetches and checks each part by its digest, never by the version. The assigner takes the digests from `version-released` for a released version, and from diff --git a/03-DESIGN/01-to-be/47-milestones/G-8-findings-heard-as-events.md b/03-DESIGN/01-to-be/47-milestones/G-8-findings-heard-as-events.md index 07ec37b0..cd16538d 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-8-findings-heard-as-events.md +++ b/03-DESIGN/01-to-be/47-milestones/G-8-findings-heard-as-events.md @@ -32,7 +32,7 @@ each module's findings as events; a module states its findings from its own trut tool-runner account stays as it is in G; no record is written for these answers, so each line that departs from a record says so in its Why. Round 3 of the review by architectural domain (2026-11-11) completed these lines.* -- §383 **An observer says its findings as events on a stream of its own; the keeper reads no observer's state.** Every +- §383 **An observer says its findings as events on a stream of its own; the keeper reads no observer's state.** (§529) Every module that writes findings is an observer (§387 names them) and says each change of a finding on its own stream `-findings` (ADR 0363 §1: under its name in capitals on the bus, its `events` `finding-written.*`, `finding-cleared.*` and `findings-stated.*`, kept 7 days and 16 MiB, its oldest message dropped when full, since the @@ -97,7 +97,7 @@ record says so in its Why. Round 3 of the review by architectural domain (2026-1 account publishes for; G changes neither that account nor its grants, a known limit. Why: the operator, 2026-11-11; modules running as the machine's account are issue 1966's (note 193), whose fix carries its check. How it is checked: none in G; the limit is named on issue 1966. -- §386 **A `*` emitter in `consumes` hears one event from every module that declares it.** A `consumes` entry +- §386 **A `*` emitter in `consumes` hears one event from every module that declares it.** (§529) A `consumes` entry `*.` takes the event `` from every module whose manifest declares it, and is the one generic form for hearing an event that many modules say; the keeper's `*.finding-written`, `*.finding-cleared` and `*.findings-stated` are the first. The manifest validator takes `*` only as the whole emitter, and only before an @@ -114,7 +114,7 @@ record says so in its Why. Round 3 of the review by architectural domain (2026-1 `*.finding-written` consumer one durable consumer on each declaring module's stream and none on a restricted stream; whole (keeper, grants part), a fixture module that declares the events after the keeper started is heard, and a keeper stopped while findings change catches up from its durable consumers at its start. -- §387 **G's change writes the finding stream into every observer's manifest.** In the same change G writes the stream +- §387 **G's change writes the finding stream into every observer's manifest.** (§529) In the same change G writes the stream `-findings` of §383 into the manifest's `streams`, and `mesh-conditions.findings-asked` into its `consumes`, for every module that declares a state `findings` on main: `artifact-store-tools`, `claude-code`, `distribution`, `gitea`, `mesh-build-go`, `mesh-config`, `mesh-healers`, `mesh-knowledge`, `mesh-provisioner`, `mesh-self-check`, `mesh-vault`, `mesh-watcher`, diff --git a/03-DESIGN/01-to-be/47-milestones/G-9-prepare-answers.md b/03-DESIGN/01-to-be/47-milestones/G-9-prepare-answers.md index 93f8a643..b24b1345 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-9-prepare-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-9-prepare-answers.md @@ -108,7 +108,7 @@ line says so.* (Q13). How it is checked: whole (indexer), `unwatch` of a placed branch refused. - §403 **A held-back ask.** A held-back ask (§83, design 46 §6) is answered in `asks` with the state `held-back` and the field `held-since`, and no field `behind`. Why: the design's choice (Q14). How it is checked: the seat's fixtures. -- §404 **The node-engine's events' bodies.** `capabilities-changed` carries the node record's stated capabilities whole +- §404 **The node-engine's events' bodies.** (§520) `capabilities-changed` carries the node record's stated capabilities whole and `at`; `composed` the module, the fingerprint and the composition's digest; `running` with `stopped` carries `stopped: true` and no fingerprint (§365), and mesh-sdk's `SeatsToPassOn` counts such a module as not running on that node, its input being §459's. Why: the design's choice (Q17). How it is checked: the seat's fixtures; unit (`SeatsToPassOn`), a row where a @@ -116,7 +116,7 @@ line says so.* ### Kept data on the bus, restore points and the reader release -- §405 **The bus module's restore points and node-backup's verbs.** The seat `mesh-broker` gains `snapshot-kept` and +- §405 **The bus module's restore points and node-backup's verbs.** (§516, §517) The seat `mesh-broker` gains `snapshot-kept` and `restore-kept`, of class `act`, their callers `holder:node-backup@same-node` alone (§337): the bus module's account is the one that moves bus data, so `restore-kept` sets the live kept data aside under a name of its own and then restores the restore point beside it, with the argument `aside-only` it sets the live data aside and restores @@ -132,7 +132,7 @@ line says so.* choice, one answer to three preparers (Q8, S9, Q-H6, C19; lead-47's ruling, 2026-11-11). How it is checked: test 36; a call of `snapshot-kept` from another node's `node-backup` holder, and a `discard-aside` from any caller but the operator, are refused by the bus; a failed restore says `restored` with `failed` and its error. -- §406 **A catch-up names its tool and arguments.** A restore point records the catch-up tool and its arguments whole +- §406 **A catch-up names its tool and arguments.** (§516) A restore point records the catch-up tool and its arguments whole (§299), and the fingerprint of the newer build it was taken before; the grants part derives the grant of that tool to `holder:node-engine@same-node` from the catalog graph's entry of that recorded fingerprint (§443). Why: the design's choice (Q-H7). How it is checked: whole (node-engine), a restore runs its recorded catch-up, granted from the @@ -208,7 +208,7 @@ line says so.* ### Readers, branches and the node-engine -- §421 **The node-engine reads the entry its want names.** The node-engine joins §196's readers: it reads, from the +- §421 **The node-engine reads the entry its want names.** (§526, §532) The node-engine joins §196's readers: it reads, from the catalog half, the catalog graph's entry of the fingerprint its want names (§443), never a manifest and never by branch, so a want needs no field `branch`, and the parts by digest from its want (§379); its reads of the indexer's and builder's states go. When the catalog half no longer holds that entry, the node-engine holds the want and its @@ -223,7 +223,7 @@ line says so.* fixtures; whole (node-engine), a call from another caller is refused by the bus. - §423 **A kept-build wait says the existing words.** A have waiting for a kept build (§271) says the existing phase and words, with no new field. Why: the design's choice (Q-H10). How it is checked: whole (node-engine). -- §424 **Each process's own build, and each bundle's.** A process says `own build unknown` in its +- §424 **Each process's own build, and each bundle's.** (§519) A process says `own build unknown` in its `_first_views` (§205); each tools bundle's composed environment carries its module's fingerprint. Why: the design's choice (S12, Q-T7). How it is checked: whole (tool runner), a bundle reads its fingerprint. - §425 **A kept call asks again what its asker names.** An asker passes the kept-call form the ask's key and the event @@ -268,9 +268,9 @@ line says so.* named; whole (knowledge base), the overview shows a group's end from `group-ended`. - §432 **The edges a `needs` names.** An artifact's `needs` names artifact references `:` of the catalog graph's edges (§95, §121). Why: the design's choice (C21). How it is checked: unit (manifest validator). -- §433 **The bus module reads the catalog half.** The bus module's `consumes` and its call of `indexer_bus_state` are +- §433 **The bus module reads the catalog half.** (§532, §538) The bus module's `consumes` and its call of `indexer_bus_state` are replaced by its view of the catalog half (§344). Why: ADR 0596 §2 (C22). How it is checked: its manifest. -- §434 **Findings name their owner and whom they wait on, and the keeper reads by events.** A finding of §33, §8 and +- §434 **Findings name their owner and whom they wait on, and the keeper reads by events.** (§541) A finding of §33, §8 and §75 carries `owner` and `waiting-on`; the keeper's other reads are by events or verbs, and no homes rows are added. Hand-written finding code in Go moves to the generated writer (§439). Why: the design's choice (C16, C17, C15's Go half). How it is checked: the code review; the keeper's manifest reads nothing. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index 1eba5159..246bce2b 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -76,7 +76,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 ten files, each read whole, its design lines numbered across them: +The part is thirteen 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. @@ -89,4 +89,5 @@ The part is ten files, each read whole, its design lines numbered across them: - [G-9-prepare-answers](G-9-prepare-answers.md): the answers to the questions of G's final prepare and develop's first questions (§391 to §455). - [G-10-develop-answers](G-10-develop-answers.md): the answers to develop's second batch of questions, the operator's and those of internal form only (§456 to §479). - [G-11-develop-answers-review](G-11-develop-answers-review.md): the operator's answers of 2026-11-11 noon, the review of §456 to §479, and develop's next questions (§480 to §515). +- [G-12-develop-answers-fourth](G-12-develop-answers-fourth.md): the operator's words of 2026-11-11 and develop's fourth batch of questions (§516 to §541). - [G-13-review-round-3](G-13-review-round-3.md): the review of §480 to §515, rounds 2 and 3 (§600 on). -- 2.54.0 From e41dbeef4ae79c5695be593fa6835fcd11e18485 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 12:40:58 +0100 Subject: [PATCH 2/9] Write the operator's answer on an Approve before G, and answer round 3's leftovers of the third batch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The operator made an old Approve that pull request's approval step, decided by the registration's policy, so §510's carried act and §608's merge wait go; round 3 of the third batch has no fourth round, so its findings are answered here (§543 to §550). Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../G-11-develop-answers-review.md | 8 +- .../G-12-develop-answers-fourth.md | 110 +++++++++++++++++- .../47-milestones/G-13-review-round-3.md | 16 +-- .../G-2-pipelines-sizes-checks-and-tools.md | 2 +- .../01-to-be/47-milestones/G-7-answers.md | 4 +- 03-DESIGN/01-to-be/47-milestones/G.md | 2 +- 6 files changed, 123 insertions(+), 19 deletions(-) diff --git a/03-DESIGN/01-to-be/47-milestones/G-11-develop-answers-review.md b/03-DESIGN/01-to-be/47-milestones/G-11-develop-answers-review.md index ea8162af..bd707d82 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-11-develop-answers-review.md +++ b/03-DESIGN/01-to-be/47-milestones/G-11-develop-answers-review.md @@ -62,7 +62,7 @@ reason. What a question of another kind leaves open waits for the operator and i ## The review of §456 to §479, round 1 -- §483 **Main's step keys stay readable while main's askers run.** (§600, §601) A build seat's holder of G keys a step whose ask +- §483 **Main's step keys stay readable while main's askers run.** (§600, §601, §544) A build seat's holder of G keys a step whose ask came on main's subject `mesh.seat..accept.`, with no asker's name, as main does, `.`, and a step whose ask names its asker as §481 says; so a builder or checker of before G, which asks on main's subject, finds its answer under `.` through the rollout one node first and after a revert. Every asker of G asks @@ -106,7 +106,7 @@ reason. What a question of another kind leaves open waits for the operator and i (review round 1, running and testing 5, health and events 3). How it is checked: whole (runner), with a fixture's small cap a step ends and is said, `full` is said and a start refused, and on `room` the entry is appended and the start taken. -- §487 **The forge module learns a commit's fingerprint from every step end.** §475's `seen..` is +- §487 **The forge module learns a commit's fingerprint from every step end.** (§548) §475's `seen..` is written from every `step-ended`, whatever its commit, a run on request's included, so a commit that failed in a run on request before its pull request was opened is known at its merge. A `seen` key is deleted by §135's rule once its commit has been a watched branch's head or the head an open pull request names and is neither, and otherwise @@ -197,7 +197,7 @@ reason. What a question of another kind leaves open waits for the operator and i `branch-unprotected` stands (§17) raises none either. Why: the watchman already times each take-in wait (develop question on §12). How it is checked: whole (watchman), a head behind a pending revision raises nothing for 60 minutes, then `take-in-waits`. -- §498 **A wait names its clock and its kind.** (§534, §608) `meshwants.Wait`, the runner's wait body and the node-engine seat's +- §498 **A wait names its clock and its kind.** (§534, §608, §542) `meshwants.Wait`, the runner's wait body and the node-engine seat's gain `clock-from` (the time the bound counts from, §266, §269) beside `bound`, and `step`, one of the acting steps of §203 or `put`, `delivery`, `kept-build`, `backup`; a put's wait (main file, *A put without hold that stands*) is `step: put` with the node, the module and `fingerprint`; a dependent's delivery wait (§376) is `step: delivery` @@ -274,7 +274,7 @@ reason. What a question of another kind leaves open waits for the operator and i Why: one message per move, the pull request where there is one (develop questions FB1, FB2 and FB7). How it is checked: whole (bridge), an admitted push writes `pushed`, a merge `merged`, and a move of an unprotected branch writes nothing. -- §510 **A merge the switch carries is performed.** (§608) A `merges` key the old bridge had not performed, an Approve the +- §510 **A merge the switch carries is performed.** (§608, §542) A `merges` key the old bridge had not performed, an Approve the operator gave before G, becomes an act (§169) that the bridge performs through `merge-pull`, judged as every merge; while `merge-pull` refuses it for missing registrations (§95) the act stays unanswered and is performed again on `registration-changed`. No other merge act is made after G (§167). Why: §169 drops nothing the operator approved 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 0ad1d984..b5860422 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 @@ -27,7 +27,9 @@ 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". -*§522 is the operator's answer of 2026-11-11 at 12:11 CET at lead-47's terminal. Every other line answers a question +*§522 and §542 are the operator's answers of 2026-11-11 at 12:11 and 12:33 CET at lead-47's terminal. §543 to §550 +answer what the four reviewers found in round 3 of [G-13](G-13-review-round-3.md) (§600 to §609), which has no round +of its own after it. Every other line answers a question develop met after [G-11](G-11-develop-answers-review.md), of internal form only (playbook 07, step 0b), each the design's choice with its reason. What a question of another kind leaves open waits for the operator and is said in the line. Questions that an earlier line already answers have no line here: develop reads that line.* @@ -348,6 +350,108 @@ line. Questions that an earlier line already answers have no line here: develop 12:11 CET at lead-47's terminal, approved the words as the design writer proposed them. How it is checked: unit (teller), each of the three said in these words with its fields filled; whole (teller, router), as §514. +- §542 **An Approve given before G is that pull request's approval step.** The operator, 2026-11-11 12:33 CET at + lead-47's terminal: approval is a step whose status each module's registration sets, never a gate of its own, so + an Approve given before G counts, once G is delivered, as that pull request's `approval` step and nothing else is + special. At G's switch the gate says, for each `merges` key the old bridge had not performed, `approval-carried` on + `mesh.seat.mesh-delivery.event.approval-carried` (`repository`, `pull`, `commit` the head the Approve named, + `approver` the operator, `at`), declared in the seat's definition, and deletes the key once the bus acknowledges + it. The runner keeps each in its old `state` section `pipelines` as `carried-approval..`, + holding the commit, and takes it as the result `passed`, caller the operator, of the `approval` step of the first + pipeline or run at that commit, then deletes it; it deletes it unused when the seat `git` says the pull request + `pull-closed` or `pull-merged`. Where the module's policy makes `approval` `required`, a pull request whose head + moved after the Approve asks a new approval, as any step whose input changed does (an `approval` step is never + carried, ADR 0666 §8); where it is `optional` or `bypass`, the carried Approve holds nothing. The merge itself is + any merge (`merge-pull`, ADR 0666 §11): the bridge performs no carried act, the gate says no `wait-held` with + `step: merge`, and the watchman writes no `merge-waits`. Every `wait-held` the runner says for an `approval` step + has its `wait-ended`, said when the step ends in any of §24's outcomes (`passed`, `declined`, `superseded`, + `cancelled`, `not asked`) or its pipeline is superseded or cancelled, so no approval wait stands without an end. + This replaces §510 and §608, and `merge` leaves §498's list. Why: the operator's answer ("you're mixing in step and + approval gates, this should be configurable"); the event, the key and the deletion are the design's (round 3, + health and events' R7). How it is checked: whole (gate, runner), a carried Approve of a head passes that head's + `approval` step with the operator as caller; with `approval` `required`, a head moved after the Approve asks a new + approval; a pull request closed deletes its carried key; every `approval` wait-held in the whole test has its + `wait-ended`; the code review finds no `merge-waits`. + +## Round 3 of the review of §480 to §515 + +- §543 **A build seat's holder refuses a kind its asker may not ask.** Callers stay per accepted kind (§338), and the + builder joins them as the one caller, beside the checker until §483's removal, of the kind `build`, as §601 has it + ask under its name. The grants part grants each asker publish on `mesh.seat..accept..` (§600) + for every queue of the seat on which a kind it is a caller of is taken, and the holder, which takes the asker from + the subject (§481), refuses an ask whose body names a kind that asker is not a caller of, answering `error` naming + the kind and the asker, and ends it with `step-ended` `failed`. This amends §338 and §600. Why: §600 puts the queue + in the subject and the kind in the body, so a subject's grant alone cannot hold a caller to its kinds (round 3, + security and upgrade, seams and tools: R1). How it is checked: unit (Go build module), a `step` published from the + indexer's account is answered `error` naming `step` and `indexer`; unit (grants part), each asker's grants are the + queues of its kinds. +- §544 **Named asks have a worker of their own.** For each queue the grants part makes, beside main's worker + `SEAT___worker` filtered to main's `mesh.seat..accept.` alone, a second worker + `SEAT___named_worker` filtered to `mesh.seat..accept..*`; the two filters do not overlap, + so the work-queue stream takes both. Only a holder of G pulls the named worker, and a holder of before G knows only + main's, so no filter is ever switched and a holder of before G that comes back from away (a laptop) after the grants + part runs takes no named ask. This replaces §601's "until every node's holder of a build seat is of G the grants + part's worker keeps main's one filter", and with it the edge from the seats' holders to the grants part, which no + manifest fact gives: the order is the asker's `invokes` of `.accept.` (kind 6, from the seat's holders + to the asker, §601), and the grants, written for a catalog revision before any want of it (*A credential and a + composition before the want*), exist before the asker runs. A named ask waits on its worker while no holder of G + runs, said by `build-capacity-waits` after 60 minutes, which corrects §483's "waits on the work queue, whose + consumer of before G takes only main's subject". At a revert, which reverts the askers before the grants part (ADR + 0656 §4), the grants part of G, when it no longer hears any asker of G, purges the subjects + `accept..*` of the stream and deletes the named workers, saying each in its journal; an ask is transport, + not kept data (as §59 has a sent object). Why: one consumer per filter, and a filter that never moves cannot be + pulled by a holder that does not read it (round 3, R2, the security reviewer's form). How it is checked: whole + (grants part, Go build module), a holder of before G started after the grants part of G takes main's asks and no + named one; a revert leaves no named worker and no named ask. +- §545 **The tool runner's artifact declares the words it states.** The artifact entry of the tool runner's manifest + takes `states`, the capability words (§488) it states; the manifest validator refuses a package artifact's `calls` + word (§605) that no artifact of the revision `states`, naming the word, and the delivery plan's edge of §605 runs to + the module whose artifact `states` that word, so no manifest names the tool runner's module. A bundle is started by + the tool runner on its channel and ends with it, so a restart of the tool runner restarts its bundles, and each + reads the stated words at its own start; no event is heard and nothing polls. This amends §605's "each time the + tool runner of its node starts". Why: the words are stated only at run time, and the plan needs them in a manifest + (round 3, seams and tools: R3). How it is checked: unit (manifest validator), a `calls` word nothing `states` + refused; unit (delivery plan), the edge to the module stating the word. +- §546 **When the builder forgets a node's toolchain.** The builder deletes its `toolchains-heard` key + `.` (§604) when the node-engine seat's `running..` says `stopped` (§365) for the + module whose catalog graph entry claims the build seat of that language (its `acts` rule `build` with `on` naming + it, §535), heard through the durable consumer it already has of that seat's `running`; never on a placement, which + is the seat `mesh-module-assigner`'s state. The state is an old `state` section of the builder, a foundation part, + one key per node and language, about 12 keys of about 0.3 KiB each, capped at 1 MiB, so it adds no objects store + to §607's count (§58 plans one only for a state of the template); `builder_toolchains` reads it and + `builder_toolchains_drop` deletes one key, with why, written in the hand-act log of the seat `mesh-hand-acts`; a + holder says the key again at its start and on each change (§492). This amends §604's "when the node leaves the + catalog's placements". Why: the builder reads no other module's state (ADR 0596 §2; round 3, seams and tools: R4). + How it is checked: whole (builder), a build module stopped on a node deletes that node's key, and the drop tool + deletes one key and logs it. +- §547 **The sum of the bus's caps is read against its limit.** The bus module reads the bus's storage limit from the + server's account information at its start and at each plan, sums the cap of every entry it plans (states, objects + stores, sent stores, streams, logs) and of every old section the catalog graph's entries declare, and shows both in + its `list` and its read tool. A plan whose sum passes the limit is refused, naming the sum and the limit, said as + its `refused` (ADR 0595 §6), whether or not the server reserves a cap at creation. When G's check measures that the + server reserves a cap when a store is made (§607), each `-objects` is planned with 16 MiB instead of 64 MiB + (about 2.4 GiB for 150 states), a state needing more naming `objects-max-mib`. This amends §607. Why: the limit is a + fact of the server, read rather than written in the design, and a sum over it fails loudly (round 3, running and + testing: R5). How it is checked: whole (bus module), a fixture server with a small limit refuses a plan past it, + naming the sum; the sum and limit are in `list`. +- §548 **The forge module writes `seen` only from the runner's steps.** §487's `seen..` is written + only from a `step-ended` whose asker is `runner`; the `step-ended` of any other ask (§602) carries `asker`, `ask`, + `node`, `outcome` and `at` and nothing of a pipeline, and the forge module writes nothing from it. This amends §487. + Why: since §602 every ask says `step-ended`, and a builder's build is no run of a commit (round 3, health and + events: R6). How it is checked: unit (forge module), a builder's failed build writes no `seen`; whole (watchman), a + `stuck` raised for an ask clears on its late `step-ended`. +- §549 **Smaller corrections from round 3.** At its start the runner appends an `unlogged` entry (§606) only after + reading back its `step-log`'s last entry of that pipeline and step, and skips it when that entry is already + logged, so a stop between the append and the clear appends nothing twice. `step-progress` and `ask-taken` (§603) + name no `keep` and go on EVENTS, kept 7 days (§396). `runner_unlogged_append` is of class `act` and takes §391's + default callers. §58 is planned with the cap §607 and §547 give. How it is checked: whole (runner), a runner + stopped between the append and the clear logs the entry once; the rest as the lines they correct. +- §550 **A publication's pipeline keeps the version.** The runner's pipeline value gains `version`, the version + `package-published` names, for a pipeline the runner started from a publication or from `outdated` (§334), and + none for any other; `show` answers it. This completes §334's "the version in the pipeline's state". Why: the + runner's contract had no field for it (develop's note on §334). How it is checked: unit (runner's contract), the + field read; whole (runner), a publish's pipeline shows its version. + ## Questions an earlier line answers These write nothing new; develop reads the line named. @@ -355,8 +459,8 @@ These write nothing new; develop reads the line named. - The tool runner's fallback of local commit 1015b38 for a foundation part's bundle goes: §407 binds that bundle to its old `objects` section, and a binding that is missing fails loudly (ADR 0595 §1) (Q-T5, T-11). - The re-read tool's callers of `seatlist.Part` are §204's for every part, the assigner's own list going (AS19). -- The bus module's cap of a state's objects store and the binding's `objects` and `sent-objects` are §513's (NATS1, - §399). +- The bus module's cap of a state's objects store is §513's as §607 and §547 amend it, and the binding's `objects` + and `sent-objects` are §513's (NATS1, §399). - The contracts' `Hold` is §194's view, the newest assignment half and the catalog half it names; the candidate's whole-half reader is §195's and §441's, read by a run on request's steps, `grants-for` and the indexer's candidate pass (contracts). diff --git a/03-DESIGN/01-to-be/47-milestones/G-13-review-round-3.md b/03-DESIGN/01-to-be/47-milestones/G-13-review-round-3.md index b1d0995d..1df54617 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-13-review-round-3.md +++ b/03-DESIGN/01-to-be/47-milestones/G-13-review-round-3.md @@ -28,7 +28,7 @@ takes §600 on, since §516 on are those of batch 4's file G-12. *These lines answer what the four reviewers by architectural domain found in round 2 of [G-11](G-11-develop-answers-review.md) (§480 to §515), the last round. Each is the design's choice with its reason.* -- §600 **A build seat's asks: subjects, workers and grants.** A build seat (`node-build-go`, `node-build-node`, +- §600 **A build seat's asks: subjects, workers and grants.** (§543, §544) A build seat (`node-build-go`, `node-build-node`, `node-build-image`) keeps its three queues of main, `delivery`, `check` and `recheck` (ADR 0525 §4, ADR 0536 §4), on its one work-queue stream. An ask is published on `mesh.seat..accept..`, `` one of the three, `` the asker's name; the ask's kind (`step`, `run`, a build) stays in its body, as on main. The @@ -43,7 +43,7 @@ takes §600 on, since §516 on are those of batch 4's file G-12. checked: unit (grants part), each queue's worker filters both subjects and each asker's grant is its own name; whole (grants part, Go build module), an ask under `runner` is taken and answered, and the runner's account publishing on main's `accept.check` is refused. -- §601 **The order of G's rollout for the build seats.** The holders of the three build seats are delivered first, +- §601 **The order of G's rollout for the build seats.** (§544) The holders of the three build seats are delivered first, then the grants part, whose workers then filter both forms (§600), then every asker of G (the runner, the indexer, the assigner, the builder) and the watchman. Until every node's holder of a build seat is of G the grants part's worker keeps main's one filter, so a holder of before G never takes a named ask, whose key it would write in main's @@ -68,20 +68,20 @@ takes §600 on, since §516 on are those of batch 4's file G-12. `stuck`, naming the seat, the asker, the ask and the node, as main's mirror does. This completes §496. Why: only the runner says `step-started` (review round 2, health and events 1). How it is checked: whole (watchman, Go build module), a builder's build whose holder stops raises `stuck` within five minutes and one that ends raises nothing. -- §603 **Where `step-progress` and `ask-taken` go.** Both are seat events and go on the stream that carries the seat's +- §603 **Where `step-progress` and `ask-taken` go.** (§549) Both are seat events and go on the stream that carries the seat's events, at that stream's own age; no stream is added and no event's age changes. `step-progress` and `ask-taken` are declared in the three build seats' definitions in `mesh-delivery-contracts`, and `step-progress` in `mesh-wants`' definition for a composition step. This amends §496's "kept on the seat's stream for a day". Why: a build seat's own stream is its asks' work queue (review round 2, running and testing 4, seams and tools' note). How it is checked: unit (definitions), each event declared. -- §604 **The builder keeps the toolchains it heard.** The builder writes each `toolchain-reported` (§492) in a state of +- §604 **The builder keeps the toolchains it heard.** (§546) The builder writes each `toolchain-reported` (§492) in a state of its own, `toolchains-heard`, one key `.` holding the version, the recipe, the digest and when it was said, and agrees the version from those keys, read back at its start, so a restart needs no node to report again. Its manifest `consumes` the three build seats' `toolchain-reported`; its tool `builder_toolchains` lists the keys; a key is deleted when the node leaves the catalog's placements (§365's reason). Why: a durable consumer does not deliver an acknowledged event again (review round 2, seams and tools 1). How it is checked: whole (builder), a restarted builder agrees the same version with no new report. -- §605 **The order after G's tool runner is derived from the package a build resolved.** A package's artifact in a +- §605 **The order after G's tool runner is derived from the package a build resolved.** (§545) A package's artifact in a manifest takes `calls`, the capability words (§488) its code calls of the tool runner; mesh-sdk's Go and npm packages name `ledger-upkeep-calls`, `sent-objects-calls` and `put-sessions` from G. For each module whose build resolved a version of such a package (§466's `resolved`), the delivery plan derives a served-word edge (kind 6) to @@ -90,7 +90,7 @@ takes §600 on, since §516 on are those of batch 4's file G-12. This amends §488's `needs`. Why: the bundle makes a session whenever a value is large, so only the package knows (review round 2, seams and tools 3, running and testing's note). How it is checked: unit (delivery plan), a module built on G's mesh-sdk is ordered after the tool runner on each node; unit (manifest validator), `calls` read. -- §606 **A step's end and its unlogged entry are one write.** The runner writes a step's end in `pipelines` with the +- §606 **A step's end and its unlogged entry are one write.** (§549) The runner writes a step's end in `pipelines` with the entry as `unlogged` in the same compare-and-set, appends it to `step-log`, then clears it; at its start it appends every `unlogged` entry of its pipelines, in order. It reads the log's fill again at each step's end, at its start, and once a minute while the log is `full`, so the entries the bus ages out say `room` with no step ending. Its tool @@ -99,7 +99,7 @@ takes §600 on, since §516 on are those of batch 4's file G-12. full log (review round 2, running and testing 2, health and events 3, seams and tools' note). How it is checked: whole (runner), a runner stopped after a step's end appends its entry at start; a full log whose oldest entries age out says `room` with no step ending. -- §607 **A state's objects store has the state's size.** Each `-objects` (§58) is planned with the state +- §607 **A state's objects store has the state's size.** (§547) Each `-objects` (§58) is planned with the state template's size, 64 MiB, unless the state's `requires` names `objects-max-mib`; the sent stores keep §477's 256 MiB. An estimated 150 states across about 120 modules give about 9.4 GiB of caps. Whether the bus counts a cap against its storage limit before anything is written is measured in G's check (the bus module's whole test reads @@ -108,7 +108,7 @@ takes §600 on, since §516 on are those of batch 4's file G-12. its owner. This amends §513 and leaves §58's "reserves nothing" to that measurement. Why: §58 names the template's size (review round 2, running and testing 3). How it is checked: unit (bus module's plan), each objects store's cap; whole (bus module), the reserved storage read after the plan. -- §608 **A carried merge that waits is a timed wait.** A merge act carried by the switch (§510) waiting for the first +- §608 **A carried merge that waits is a timed wait.** (§542) A merge act carried by the switch (§510) waiting for the first registrations is said by the gate as `wait-held` with `step: merge` (added to §498's list), naming the pull request and the head the Approve named, bounded at 60 minutes; past it the watchman writes `merge-waits`, which the condition keeper raises and `wait-ended` clears. It merges only the head the Approve named: a pull request moved diff --git a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md index c8f6b8aa..59f768e6 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md +++ b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md @@ -41,7 +41,7 @@ One file of milestone G's part of [design 47](../47-delivery-from-commit-to-deli compare-and-set; the object it replaced stays, as one whose key was deleted does, listed as `key gone` since no key names it, and only the drop tool removes either, on a person's word: the mesh removes kept data only through `drop` and `recreate`, each on a person's word (ADR 0595 §4; ADR 0666 §16). -- §58 **One object store per state, granted to its owner.** (§513) Each state asked by the template `state` gets its object store +- §58 **One object store per state, granted to its owner.** (§513, §549) Each state asked by the template `state` gets its object store beside it, `-objects`, a template form of ADR 0595 that the bus module provides and the grants part grants to the state's owner alone (ADR 0666 §16); a foundation part's large values go in an old `objects` section of its own. Its size is the template's (ADR 0595 §3). An object store holds no space until a value is written to it, so the many diff --git a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md index 37b8d268..92fde3cb 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -124,7 +124,7 @@ record it follows from, and replaces the builder's own choice where one was made `take-in.taken` (ADR 0666 §13), which ADR 0596 §1 lets any module subscribe to; the indexer's name stands in that manifest only, never in the runner's code. Why: ADR 0666 §13, in force, names that event. How it is checked: the code review reads that no module name stands in the runner's code (§7). -- §334 **A publication's pipelines and their key.** `package-published` carries the package's artifact reference +- §334 **A publication's pipelines and their key.** (§550) `package-published` carries the package's artifact reference (`:`, from the catalog entry), its version and digest, the watched branch and the source revision its build was made from. The runner asks `outdated` for that package on that branch and starts a pipeline for each dependent `outdated` names behind (§53), keyed by that branch, that source revision and the dependent module, the version in the pipeline's state; a `take-in.taken` of the same revision and module is the @@ -165,7 +165,7 @@ record it follows from, and replaces the builder's own choice where one was made and restore a module's kept data on the bus name `holder:node-backup@same-node`. Why: only that holder takes them (*A backup before the apply: kept data on the bus*). How it is checked: a call from another node's `node-backup` holder is refused by the bus. -- §338 **Callers on accepted kinds.** (§480, §481) An accepted kind of a work queue carries `callers` as a verb does, and the grants part +- §338 **Callers on accepted kinds.** (§480, §481, §543) An accepted kind of a work queue carries `callers` as a verb does, and the grants part grants publish on its subject to them alone: the build seats' `step` the runner; `run` the runner, the indexer and the assigner, and the checker until its entry is removed with its buckets and bus account (ADR 0667 §12); `mesh-wants`'s `step` the runner. Verbs: `steps` the runner and the indexer (its seed); `drop` the runner and the builder; both diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index 246bce2b..fd39d942 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -89,5 +89,5 @@ The part is thirteen files, each read whole, its design lines numbered across th - [G-9-prepare-answers](G-9-prepare-answers.md): the answers to the questions of G's final prepare and develop's first questions (§391 to §455). - [G-10-develop-answers](G-10-develop-answers.md): the answers to develop's second batch of questions, the operator's and those of internal form only (§456 to §479). - [G-11-develop-answers-review](G-11-develop-answers-review.md): the operator's answers of 2026-11-11 noon, the review of §456 to §479, and develop's next questions (§480 to §515). -- [G-12-develop-answers-fourth](G-12-develop-answers-fourth.md): the operator's words of 2026-11-11 and develop's fourth batch of questions (§516 to §541). +- [G-12-develop-answers-fourth](G-12-develop-answers-fourth.md): the operator's words of 2026-11-11 and develop's fourth batch of questions (§516 to §550). - [G-13-review-round-3](G-13-review-round-3.md): the review of §480 to §515, rounds 2 and 3 (§600 on). -- 2.54.0 From e504dac53499dd17ff71e7ad3a044f234b173afa Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 12:42:09 +0100 Subject: [PATCH 3/9] Write the operator's answers of 12:41 on restore points, the checker's drop and an empty step status Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../G-1-pipelines-registration-and-steps.md | 2 +- .../G-12-develop-answers-fourth.md | 35 ++++++++++++++++--- .../01-to-be/47-milestones/G-7-answers.md | 2 +- 03-DESIGN/01-to-be/47-milestones/G.md | 2 +- 4 files changed, 33 insertions(+), 8 deletions(-) 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 5bb70ef3..5671a93a 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 @@ -200,7 +200,7 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l `operator-channel`'s accepted kind `ask`, on its work queue by the runner's name (design 46 §10, *Asks, warrants and answers on the bus*; the seat has no verb `ask`), and heard on its event `decided` (ADR 0666 §7, §10). A step with nothing to do ends at once as `nothing to run`, never as passed. -- §23 **The policy.** Each step's status, per module and per artifact, is `required` (runs; holds the delivery until it +- §23 **The policy.** (§553) Each step's status, per module and per artifact, is `required` (runs; holds the delivery until it passes), `optional` (runs; a failure is shown and may be accepted) or `bypass` (nothing runs; said as bypassed), kept in the registration and never in a manifest: the manifest validator refuses a manifest that names a status (ADR 0666 §7; research 086, R3). A `required` step that ends `nothing to run` holds the delivery and names the step. 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 b5860422..900f39d1 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 @@ -27,7 +27,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". -*§522 and §542 are the operator's answers of 2026-11-11 at 12:11 and 12:33 CET at lead-47's terminal. §543 to §550 +*§522, §542 and §551 to §553 are the operator's answers of 2026-11-11 at 12:11, 12:33 and 12:41 CET at lead-47's terminal. §543 to §550 answer what the four reviewers found in round 3 of [G-13](G-13-review-round-3.md) (§600 to §609), which has no round of its own after it. Every other line answers a question develop met after [G-11](G-11-develop-answers-review.md), of internal form only (playbook 07, step 0b), each the @@ -36,7 +36,7 @@ line. Questions that an earlier line already answers have no line here: develop ## Kept data on the bus: restore points and what is set aside -- §516 **A restore point's bytes, and the forms of `snapshot-kept` and `restore-kept`.** The seat `mesh-broker`'s two +- §516 **A restore point's bytes, and the forms of `snapshot-kept` and `restore-kept`.** (§551) The seat `mesh-broker`'s two verbs (§405) take and answer the forms its definition in mesh-contracts gives them, which this line makes the design's: `snapshot-kept` takes `module`, `kept` (the kept data's local name), `form`, `fingerprint`, `why` and `catch-up` as `{tool, arguments}` (§451), and answers `point` (the module, the kept data, its form, the @@ -58,8 +58,7 @@ line. Questions that an earlier line already answers have no line here: develop aside's `id` is that directory's name, its `name` the bus name it was live under, its `size` its bytes on disk; nothing of an aside stays on the bus, so it takes none of the bus's storage limit, and the definition's description of `name` reads "the bus name it was live under". `discard-aside` removes that directory and nothing - else. No part removes a restore point in G: how long one is kept waits for the operator (issue 1921's note), and - until the answer is written here every point stays. This completes §405 and §406. Why: ADR 0235 has the bus + else. A restore point is removed only as §551 says. This completes §405 and §406. Why: ADR 0235 has the bus module's account write the bus's snapshots into its own directory and the backup holder back that directory up; the server restores a stream only where no stream of that name exists, so the live data is set aside as a snapshot before the point takes its name (develop questions BKP1, BKP3 and NATS4's argument names). How it is @@ -73,7 +72,7 @@ line. Questions that an earlier line already answers have no line here: develop 0619 §1, ADR 0666 §4): a point under `point....` with the answer's `point` whole, written when `snapshot-kept` answers done; an aside under `aside...` with the answer's `aside` whole, written when `restore-kept` answers one, each token escaped as a key's name is (§73). The holder deletes an - aside's key when `discard-aside` answers done, and deletes no other key in G (§516). `backed-up` lists both from + aside's key when `discard-aside` answers done, and a point's key when it hears that point's `point-removed` (§551). `backed-up` lists both from that state, each with its size (§302), and the holder's read tool of its state reads it. The seat `mesh-broker` gains no read verb. The holder's old `state` entry `good` moves to `requires` in the same change, keeping its local name and settings, as ADR 0595 §11 moves a module G touches. Why: the holder took each from its own call's answer, @@ -452,6 +451,32 @@ line. Questions that an earlier line already answers have no line here: develop runner's contract had no field for it (develop's note on §334). How it is checked: unit (runner's contract), the field read; whole (runner), a publish's pipeline shows its version. +## The operator's answers of 12:41 CET + +- §551 **A restore point is removed after 90 days, never the newest.** The operator, 2026-11-11 12:41 CET at lead-47's + terminal. The bus module removes a restore point's directory (§516) 90 days after its `at`, as long as delivery + records are kept, unless it is the newest point of its module and kept data, which it never removes; it checks at + its start, once a day and at each `snapshot-kept`, on its own clock and its own directory. Each removal is said as + the seat `mesh-broker`'s event `point-removed` (`module`, `kept`, `form`, `fingerprint`, `snapshot`, `at`), declared + in its definition; the backup holder consumes it and deletes that point's key in `bus-points` (§517), and its + repository's copy goes by the repository's own rotation (ADR 0235 §4). Data set aside is removed only by + `discard-aside` (§516). This completes §516 and answers issue 1921's note on the retention of restore points. Why: + the operator's answer; the event, the checks and the holder's deletion are the design's. How it is checked: whole + (bus module, backup holder), a point older than 90 days is removed and said and its key deleted, and the newest + point of a kept data, older than 90 days, stays. +- §552 **The checker may drop its own asks until its removal.** The operator, 2026-11-11 12:41 CET at lead-47's + terminal: the build seats' verb `drop` (§338) gains the checker as a caller until its entry is removed with its + buckets and bus account (ADR 0667 §12), as `run` has it, so the validator accepts the checker's manifest's + `invokes` of `seat:node-build-go.drop` and `seat:node-build-node.drop`. This amends §338. How it is checked: unit + (manifest validator), the checker's manifest accepted; whole (grants part), the checker's drop granted and another + module's refused. +- §553 **A step status left empty is `optional`.** The operator, 2026-11-11 12:41 CET at lead-47's terminal: a step + whose status a registration's policy (module-level `policy.steps`, or an artifact's) leaves empty is read as + `optional`: it runs, and a failure is shown and holds nothing (§23); `register` and `policy` accept such a policy + and refuse nothing for it, and the runner, `registrations` and `show` say the step `optional`. This completes §23. + How it is checked: unit (runner), a step with no status runs and its failure holds no want; unit (mesh-catalog's + contract), a registration with an empty status accepted. + ## Questions an earlier line answers These write nothing new; develop reads the line named. diff --git a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md index 92fde3cb..0ebd9f32 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -165,7 +165,7 @@ record it follows from, and replaces the builder's own choice where one was made and restore a module's kept data on the bus name `holder:node-backup@same-node`. Why: only that holder takes them (*A backup before the apply: kept data on the bus*). How it is checked: a call from another node's `node-backup` holder is refused by the bus. -- §338 **Callers on accepted kinds.** (§480, §481, §543) An accepted kind of a work queue carries `callers` as a verb does, and the grants part +- §338 **Callers on accepted kinds.** (§480, §481, §543, §552) An accepted kind of a work queue carries `callers` as a verb does, and the grants part grants publish on its subject to them alone: the build seats' `step` the runner; `run` the runner, the indexer and the assigner, and the checker until its entry is removed with its buckets and bus account (ADR 0667 §12); `mesh-wants`'s `step` the runner. Verbs: `steps` the runner and the indexer (its seed); `drop` the runner and the builder; both diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index fd39d942..3e8bd514 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -89,5 +89,5 @@ The part is thirteen files, each read whole, its design lines numbered across th - [G-9-prepare-answers](G-9-prepare-answers.md): the answers to the questions of G's final prepare and develop's first questions (§391 to §455). - [G-10-develop-answers](G-10-develop-answers.md): the answers to develop's second batch of questions, the operator's and those of internal form only (§456 to §479). - [G-11-develop-answers-review](G-11-develop-answers-review.md): the operator's answers of 2026-11-11 noon, the review of §456 to §479, and develop's next questions (§480 to §515). -- [G-12-develop-answers-fourth](G-12-develop-answers-fourth.md): the operator's words of 2026-11-11 and develop's fourth batch of questions (§516 to §550). +- [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). -- 2.54.0 From 6d0506402dc80c2e785ce9eb5401d623275a186d Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 12:57:01 +0100 Subject: [PATCH 4/9] Put restoring kept data on a recovery module, and answer round 1 of the review of batch 4 The operator chose a module of its own for snapshotting and restoring a module's kept data on the live bus, so the bus module and the snapshot account keep their narrow grants; the reviewers' musts are written into the lines they name, and the new lines go to G-14 so G-12 stays within a part's size. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- 00-META/glossary.md | 6 + .../G-12-develop-answers-fourth.md | 315 +++++++++++------- .../G-14-recovery-module-and-review.md | 128 +++++++ .../G-4-revision-view-and-steps.md | 2 +- .../G-5-acting-parts-check-and-kept-data.md | 10 +- .../01-to-be/47-milestones/G-7-answers.md | 4 +- .../47-milestones/G-9-prepare-answers.md | 8 +- 03-DESIGN/01-to-be/47-milestones/G.md | 4 +- 8 files changed, 342 insertions(+), 135 deletions(-) create mode 100644 03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md diff --git a/00-META/glossary.md b/00-META/glossary.md index b41fddcb..fc4eecb9 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -974,6 +974,12 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564, 0668. at one moment; **restore** — bringing it back beside the live data, never over it. - **stream snapshot** — the bus's own backup of each stream, taken by the module holding `mesh-broker` (ADR 0235). +- **recovery module** — the holder of the seat `mesh-recovery`, held once for the mesh: under an account of its own it + 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`. + *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 `..` written only when it changes; the holder of `node-backup`, mounts, nfs-server and the 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 900f39d1..e597b32c 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 @@ -10,6 +10,7 @@ decisions: - "ADR 0660" - "ADR 0656" - "ADR 0636" + - "ADR 0626" - "ADR 0619" - "ADR 0618" - "ADR 0614" @@ -17,6 +18,7 @@ decisions: - "ADR 0595" - "ADR 0564" - "ADR 0505" + - "ADR 0404" - "ADR 0363" - "ADR 0240" - "ADR 0235" @@ -32,63 +34,73 @@ answer what the four reviewers found in round 3 of [G-13](G-13-review-round-3.md of its own after it. Every other line answers a question develop met after [G-11](G-11-develop-answers-review.md), of internal form only (playbook 07, step 0b), each the design's choice with its reason. What a question of another kind leaves open waits for the operator and is said in the -line. Questions that an earlier line already answers have no line here: develop reads that line.* +line. The operator's answer of 12:55 CET (the recovery module, §554 and §555) and the +review of §516 to §553, round 1 (§556 to §558), are in [G-14](G-14-recovery-module-and-review.md); the findings of +that round are written into the lines they name, here. Questions that an earlier line already answers have no line here: develop reads that line.* ## Kept data on the bus: restore points and what is set aside -- §516 **A restore point's bytes, and the forms of `snapshot-kept` and `restore-kept`.** (§551) The seat `mesh-broker`'s two - verbs (§405) take and answer the forms its definition in mesh-contracts gives them, which this line makes the - design's: `snapshot-kept` takes `module`, `kept` (the kept data's local name), `form`, `fingerprint`, `why` and - `catch-up` as `{tool, arguments}` (§451), and answers `point` (the module, the kept data, its form, the - fingerprint, `snapshot`, `consumers` with each durable consumer's stream, name and acknowledged sequence, `size` in - bytes, `at` and `catch-up`) or `error` alone; `restore-kept` takes `module`, `kept`, `form`, `fingerprint`, `why` - and one of `snapshot` (the restore point's name), `aside-only` or `discard-aside` (an aside's `id`), and answers - `restored`, `aside` and `error`. No argument `local-name` or `from` is taken, and a catch-up is never text. The bus - module keeps a restore point in its own `snapshots` directory, which ADR 0235 §1 to §3 give it for the bus's dump: - the server's snapshot of each bus object of the kept data (its stream, or the stream under its bucket or object - store), taken one at a time with ADR 0235 §10's pacing and §11's bounds, and the point's record, in the directory - `kept///-`, each token escaped as a key's name is (§73); that name is the point's - `snapshot`. The holder of `node-backup` backs that directory up as it backs up `snapshots` (ADR 0235 §3), so its - copy is the point's safeguard, never the point: when its `restore` finds the point's directory missing on the bus - module's node, it brings it back from its newest repository snapshot that holds it, under the same name, and only - then calls `restore-kept`; when none holds it, `restored` says `failed` with `restore point missing`, and §300 holds - the revert. `restore-kept` sets the live kept data aside by taking the server's snapshot of each of its bus objects - into `aside///` in the same directory (`at` the UTC time written `20261111T121200Z`), only then - removes the live objects, restores the point under their names and sets each consumer to its recorded sequence. The - aside's `id` is that directory's name, its `name` the bus name it was live under, its `size` its bytes on disk; - nothing of an aside stays on the bus, so it takes none of the bus's storage limit, and the definition's - description of `name` reads "the bus name it was live under". `discard-aside` removes that directory and nothing - else. A restore point is removed only as §551 says. This completes §405 and §406. Why: ADR 0235 has the bus - module's account write the bus's snapshots into its own directory and the backup holder back that directory up; - the server restores a stream only where no stream of that name exists, so the live data is set aside as a - snapshot before the point takes its name (develop questions BKP1, BKP3 and NATS4's argument names). How it is - checked: unit (mesh-broker's contract), each verb's input and answer read, an input naming `from` refused; whole - (bus module, backup holder), a point is taken, its directory removed, brought back from the repository and - restored, the live data's aside listed with its size and the consumer at its recorded sequence; a point missing - from both says `restore point missing`. -- §517 **`backed-up` reads the restore points and the data set aside from the backup holder's own state.** The holder of - `node-backup` on the bus module's node keeps what `snapshot-kept` and `restore-kept` answered it in its state - `bus-points`, asked in its `requires` by the template `state` (ADR 0595 §1; the holder is no foundation part, ADR - 0619 §1, ADR 0666 §4): a point under `point....` with the answer's `point` whole, - written when `snapshot-kept` answers done; an aside under `aside...` with the answer's `aside` - whole, written when `restore-kept` answers one, each token escaped as a key's name is (§73). The holder deletes an - aside's key when `discard-aside` answers done, and a point's key when it hears that point's `point-removed` (§551). `backed-up` lists both from - that state, each with its size (§302), and the holder's read tool of its state reads it. The seat `mesh-broker` - gains no read verb. The holder's old `state` entry `good` moves to `requires` in the same change, keeping its local - name and settings, as ADR 0595 §11 moves a module G touches. Why: the holder took each from its own call's answer, - so the list is its own fact (ADR 0596 §2), and a read verb on `mesh-broker` would answer what the caller already - holds (develop question BKP2). How it is checked: whole (backup holder), a restore's aside is listed by `backed-up` - with its size, is gone after `discard-aside`, and a restart of the holder lists the same. +- §516 **A restore point's bytes, and the forms of `snapshot-kept` and `restore-kept`.** (§551, §554) The seat + `mesh-recovery`'s two verbs (§554) take and answer the forms mesh-contracts' definition gave the seat `mesh-broker`'s, + which move to `mesh-recovery` and which this line makes the design's: `snapshot-kept` takes `module`, `kept` (the + kept data's local name), `form`, `fingerprint`, `why` and `catch-up` as `{tool, arguments}` (§451), and answers + `point` (the module, the kept data, its form, the fingerprint, `snapshot`, `consumers` with each durable consumer's + stream, name and acknowledged sequence, `size` in bytes, `at` and `catch-up`) or `error` alone; `restore-kept` takes + `module`, `kept`, `form`, `fingerprint`, `why`, `from-backup` (yes or no) and one of `snapshot` (the restore point's + name), `aside-only` or `discard-aside` (an aside's `id`), and answers `restored`, `aside`, `missing` (true when the + point is not in the recovery module's directory) and `error`. No argument `local-name` or `from` is taken, and a + catch-up is never text. + - **Where a point lives.** The recovery module takes a point through the bus's stream snapshot under its own account + (§554), one bus object of the kept data at a time (its stream, or the stream under its bucket or object store), + with ADR 0235 §10's pacing and §11's bounds, and writes it with the point's record in its own data directory, + `points///-`, each token escaped as a key's name is (§73) and a token that is + empty, `.` or `..` refused; that name is the point's `snapshot`. + - **Its copy.** That directory is an item of the recovery module's manifest `data`, backed up by the holder of + `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. + - **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 (`at` the UTC time written + `20261111T121200Z`, with `-2`, `-3` and on added when that name exists), only then removes the live objects, + restores the point under their names and sets each consumer to its recorded sequence. Each step is written first + in the aside's record (`set aside`, `removed`, `restored`): when a step after the removal fails, `restore-kept` + restores the aside under the live names and sets the consumers back before it answers `failed`, and at its start + the recovery module does the same for every aside whose record ends at `removed`, so a failed or stopped restore + never leaves the kept data gone. The aside's `id` is that directory's name, its `name` the bus name it was live + under, its `size` its bytes on disk; nothing of an aside stays on the bus. `discard-aside` removes that directory + and nothing else. A restore point is removed only as §551 says. + + This completes §405 and §406. Why: the operator's answer of 12:55 CET (§554) puts the taking and restoring of a + module's kept data on the live bus on a module of its own; the server restores a stream only where no stream of that + name exists, so the live data is set aside as a snapshot before the point takes its name (develop questions BKP1, + BKP3 and NATS4's argument names; review round 1, running and testing 1, seams and tools' notes). How it is checked: + unit (mesh-recovery's contract), each verb's input and answer read, an input naming `from` refused; whole (recovery + module, backup holder, bus), a point is taken, its directory removed, brought back with `from-backup` and restored, + the live data's aside listed with its size and the consumer at its recorded sequence; a point missing from both says + `restore point missing`; a restore made to fail after the removal, and one stopped there and started again, leave + the live data as before. +- §517 **The recovery module lists what it holds.** The seat `mesh-recovery`'s read verb `kept-points` (class `read`, + §391's default callers) lists every restore point and aside in its directory with its module, kept data, form, + fingerprint, size and `at`, and its tools are `recovery_kept_points`, which reads the same, and + `recovery_expire_points`, which runs §551's removal now (class `act`, §391's default callers, written in the + hand-act log). The directory is the one place they are kept, so no copy can list less: the backup holder keeps no + state of them (its `backed-up` lists the recovery module's item as any item, §302), and no `bus-points` state is + made. Why: a module's tools read everything it holds (*Tools are how a module is spoken to*), and the list is the + holder's own fact (ADR 0596 §2; develop question BKP2; review round 1, seams and tools 4). How it is checked: whole + (recovery module), `kept-points` lists a point and an aside with their sizes, an aside is gone after + `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 - `node-backup`'s `ended...` and `restored...` (§405), 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. A reader hears one module's events by the subject with + escaped as a key's name is (§73), refusing an empty part, `.` and `..`. A reader hears one module's events by the subject with that module's tokens and `>` after them, and every module's by `*` for each part. Such an event has no key of its own and is kept as its definition's `keep` says, or on EVENTS when it names no `keep` (§396). The holder's account publishes it as a seat's holder publishes its seat's events today; no grant changes. Why: §397 fixes the subject's words, not how a part is filled (develop question NATS4's wire). How it is checked: unit (seat generator), a subject - of three parts made in order and an empty part refused; whole (backup holder), `ended` heard by a reader of one + of three parts made in order and an empty part refused; whole (recovery module), `ended` heard by a reader of one module's subject. ## The tool runner and its bundles @@ -98,17 +110,23 @@ line. Questions that an earlier line already answers have no line here: develop §345's put session and §490's get session run on that channel, as every `mesh/` call of a bundle does (ADR 0595 §9); `` in §457, §489 and §490 names that call, and no bus subject. A value at or under the limit is put and got by one call, as today. Over it: + - **Whose object.** A session's `name` names an object of the bundle's own module alone: the tool runner resolves + it in that module's binding (its state's `-objects` or its sent store), and refuses a name of another + module's, naming it. A put's whole size is bounded by that store's cap, and a put whose chunks pass it is refused + at the chunk that does, naming the store. - **Put.** The bundle sends each chunk as the call `mesh/objects.put-chunk` with `name` (the object), `call` (the bundle's id for this put), `seq` (from 1, one more each chunk), `last` (true on the last chunk alone) and `data` (the chunk's bytes in base64, at most half the bus's maximum payload before encoding, §457). The tool runner answers each with `{seq}` or `{error}`, the bundle sends the next only after a `{seq}`, and the answer to the last is the put's answer; a `seq` that is not one more than the last taken is answered `{error}` naming both, and a put - not sealed within the call's bound is dropped (§345). The tool runner writes the whole as one object through the - object store's own chunking. + not sealed within the call's bound is dropped (§345). The tool runner writes each chunk into the store as it takes it, through the + object store's own chunking, holding no more than one chunk in memory, and seals the object on the last. - **Get.** The tool runner answers the get call `{chunked: , size, digest, call}` (§490), and the bundle asks each chunk in order by the call `mesh/objects.get-chunk` with `call` and `seq`, answered `{seq, last, data}` or `{error}`; the bundle checks the whole against `digest`, and a get not completed within the call's bound fails, - naming the object. + naming the object. The tool runner drops a get session it holds when the call's bound passes, or the bundle's + channel closes, whichever comes first. + - **The bound.** The call's bound counts once for the whole session, from its first chunk, never per chunk. The headers `Mesh-Put-Seq` and `Mesh-Put-Last` of §457 are these fields `seq` and `last`. Nothing of a session is on the bus, so §489's grants are not made and the grants part grants nothing for it. A module's process, which @@ -117,8 +135,9 @@ line. Questions that an earlier line already answers have no line here: develop calls. This amends §345, §457, §489 and §490; in the get the bundle asks each chunk, since on that channel every `mesh/` call is the bundle's. Why: the operator's answer to develop question D-13, chunks in order, each acknowledged before the next, is kept whole on the one channel a bundle has (develop questions T-5 and T-7). How it - is checked: whole (mesh-sdk, tool runner), a value of a hundred times the limit is put and got back whole by a - bundle, its digest checked, and a chunk out of order fails the put naming both sequences; the grants part's table + is checked: whole (mesh-sdk, tool runner), a value of a hundred times the limit, into a fixture state whose + `objects-max-mib` holds it, is put and got back whole by a bundle, its digest checked, a put naming another + module's object is refused, and a chunk out of order fails the put naming both sequences; the grants part's table test grants no session subject. - §519 **A bundle's fingerprint is in the environment the node-engine composes.** The node-engine writes `MESH_FINGERPRINT`, the fingerprint of the want it applies for the bundle's module, into each tools bundle's @@ -133,25 +152,31 @@ line. Questions that an earlier line already answers have no line here: develop - §520 **`running` says a build's health, and the assigner reads health from it alone.** The node-engine seat's `running..` gains `healthy` (true or false), the health of the build that runs as the node-engine's have holds it from the module's declared check (ADR 0240), and the node-engine says `running` again on the same - subject each time `healthy` changes, so the stream, kept last per subject, holds each build's newest health. - `stopped` (§365, §404) is said only when the have no longer holds the module, never for an unhealthy build. The + subject each time `healthy` changes, and for every module its have holds at its own start, so the stream, kept last per subject, holds each build's newest health. + `stopped` (§365, §404) is said only when the have no longer holds the module, never for an unhealthy build. A `running` with no + `healthy`, said by a node-engine of before G, reads as healthy while it stands, as `running` meant on main. The assigner reads a provider's health for rule 5, the canary's for rules 3 and 4, and a put's end from these events - alone, through its durable consumer that starts from the last message per subject (§199), and reads no have: a - provider of which nothing was heard since that consumer began counts as not healthy, so its consumers' new wants + alone, read as §556 has it, and reads no have: a + provider of which nothing was heard counts as not healthy, so its consumers' new wants are held as rule 5 holds any, and nothing falls back to the have. `SeatsToPassOn` reads no `healthy` (§459): an unhealthy holder still holds its seat. The assigner's `reads` of the node-engine's have go. This amends §309 and §404. Why: the assigner reads no other module's state (ADR 0596 §2), and a stopped build and an unhealthy one are told apart (develop questions AS15 and AS16). How it is checked: unit (the node-engine seat's fixtures), `running` with `healthy` read; whole (assigner, node-engine), a provider turning unhealthy holds a consumer's new want and its - recovery lets the want go, with the assigner's account holding no read of the have. + recovery lets the want go, with the assigner's account holding no read of the have; whole (assigner, a + node-engine of before G), its provider's `running` with no `healthy` holds no consumer's want; a restarted assigner + holds nothing it did not hold before. - §524 **The node-engine checks a want's parts by their digests.** A composition's archive, process and container - resources name a source and a digest, not an artifact, so the node-engine matches no resource to a `parts` key: it - applies a want only when the digest of every resource it fetched is among the values of the want's `parts`, and + resources name a source and a digest, not an artifact, so the node-engine matches no resource to a `parts` key. A want + 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 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. + 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. - §525 **A node-engine says it reads the want's new fields.** A node-engine of G states the capability `want-parts` in its node record's capabilities (ADR 0614 §4, ADR 0618 §2), and the assigner writes `version`, `parts` and `kept-on` (§526) into a node's want only while that node's record states it; that is §379's "once that node runs G's @@ -168,11 +193,12 @@ line. Questions that an earlier line already answers have no line here: develop the catalog half of their view: the module whose entry claims the seat `node-engine`, which §508 makes one per revision; the manifest validator, beside §508's refusal of a second claim, refuses a revision in which no module claims it, naming the seat. §364's, §365's and §508's "from the seat list" read so, the condition keeper's too. With - no entry claiming the seat, which only a revision from before G can show, the node-engine applies nothing of that - revision and its have says `entry-missing`, naming the seat. Why: §364 and §508 have the node-engine claim its seat + no entry claiming the seat, which only a revision from before G shows, as at a revert of G, the node-engine's own + module is the module of the catalog graph entry its own build's fingerprint names (§443, §205), so a revert of the + node-engine applies. Why: §364 and §508 have the node-engine claim its seat as one module, so the claim names it (develop question H-15). How it is checked: unit (manifest validator), a revision with no claim of `node-engine` refused; whole (node-engine), its guard protects the module its catalog - half names, and the code review finds no fixed name (§428). + half names, and with a revision of before G the module its own fingerprint's entry names, and the code review finds no fixed name (§428). ## Findings and the condition keeper @@ -186,8 +212,13 @@ line. Questions that an earlier line already answers have no line here: develop - **Every other part** asks both in its `requires`: the state `findings` by the template `state`, keeping its local name and carrying its settings as options (ADR 0595 §11), and the stream by the template `events` under the local name `finding-events`, with `max-age-days` 7 and `max-mib` 16, which the template drops oldest when full (ADR 0595 - §3), published on its binding's subject, `mesh.bus..finding-events..`. `mesh-healers`, whose - old `streams` declares `mesh-healers-findings` on main, moves it so in G's change. + §3), its entry listing the three finding events in its field `events`, which the catalog graph entry carries + (§365), published on its binding's subject, `mesh.bus..finding-events..`. + - **`mesh-healers`**, whose old `streams` declares `mesh-healers-findings` on main, moves it so in G's change, as + ADR 0595 §11 moves a module G touches. Its old stream stays on the bus as an orphan that `orphans` lists, removed + only by the operator's `drop` (ADR 0595 §4, §5); the grants part deletes the keeper's consumer on it once the + healers' entry no longer declares it (§386), and the healers say `findings-stated` at their start on the new + stream (§384), so the keeper loses no finding. The manifest validator applies ADR 0595 §11's count of old sections to no part of its foundation list (ADR 0619 §2), so the tool runner's and the checker's manifests are not refused for it, and accepts in a foundation part's @@ -196,8 +227,9 @@ line. Questions that an earlier line already answers have no line here: develop list, fixed in the validator (ADR 0619 §3). The validator refuses a manifest whose state `findings` has no finding stream in its part's form, or no `consumes` of `mesh-conditions.findings-asked` (§387). The writer (§461) is handed the subjects from the part's binding or its old stream, as it is handed every subject. The catalog graph entry - carries the finding stream in either form (§365); the grants part makes the keeper's consumer of §386 on either - stream and grants each part publish on its own finding stream's subjects of its own node only, in either form; the + carries the finding stream in either form (§365); the grants part makes the keeper's consumer of §386 on an + old stream at the revision that declares it, and on a template stream when it hears the bus module's `created` for + `finding-events` and grants each part publish on its own finding stream's subjects of its own node only, in either form; the keeper takes the observer and the node from either subject. This amends §383, §386 and §387, whose "into the manifest's `streams`" holds for a foundation part alone. Why: ADR 0666 §4 and ADR 0595 §11 put a part's kept data in the templates unless it is a foundation part, and ADR 0619 §2 keeps the foundation's old sections whole; the @@ -205,32 +237,42 @@ line. Questions that an earlier line already answers have no line here: develop name per module (develop questions on §387, both validator refusals). How it is checked: unit (manifest validator), a foundation part's old `findings` and finding stream accepted, a non-foundation part's old sections refused and its template form accepted; the catalog's validation at G's heads refuses none of §387's manifests; whole (keeper, a - template observer and the tool runner), both forms' events raise a condition. + template observer and the tool runner), both forms' events raise a condition; whole (keeper, healers), a healers' + finding raised before their move is still raised after it. - §540 **The keeper hands a failed head to the operator after two hours.** The condition keeper raises `pipeline-failed` (§33) with `owner` the commit's author, as the runner's finding names it, and two hours after the finding's `since` sets the condition's `owner` to `operator-channel`, the seat the operator is asked through (a seat's name, §451), and says that change as it says any change of a condition; the runner's finding keeps the - author. The keeper times the two hours on its own conditions from `since`, so after its restart it times them from + author. §451's `owner` and `waiting-on` gain a third form, `forge:`, the commit's author as the forge + names it. A `pipeline-failed` for a newer failing head is a finding of its own, its id naming that commit, so it is + its new author's with two hours of its own. The keeper's tool `conditions_owner` sets a condition's `owner` now, of + class `act` with §391's default callers, written in the hand-act log. The keeper times the two hours on its own conditions from `since`, so after its restart it times them from `since` again. Why: §33 has the keeper raise it as the author's, then the operator's (ADR 0505 §5, §6; develop question COND7). How it is checked: whole (keeper), a `pipeline-failed` is the operator's two hours after its - `since`, and so after a restart of the keeper. + `since`, and so after a restart of the keeper; unit (mesh-conditions' contract), `forge:` read. - §541 **The keeper hears what a node runs and the healers' attempts as events.** The keeper's `reads` of `node-engine.have` and `mesh-healers.attempts` go (§434). - **What a node runs.** The keeper consumes the node-engine seat's `composed`, `running` (with `healthy`, §520, and - `stopped`) and `wait-held` and `wait-ended` (§499), from the last message per subject (§199), and compares them - with the wants it hears from the assigner. The node-engine says `wait-held`, with `step: compose` and the reason, - for each hold its have writes (§300's `restore point missing`, §421's and §527's `entry-missing`, §524's - `parts-differ`, §499's kept build), and `wait-ended` when the hold ends. + `stopped`) and `wait-held` and `wait-ended` (§499), read as §556 has it, and compares them with the wants it hears + from the assigner, to hold a module's findings while it is delivered (ADR 0564 §13), as it did from the haves. + The node-engine says `wait-held` on `mesh.seat.node-engine.event.wait-held..`, with `step: compose` + and the reason, for each hold its have writes (§300's `restore point missing`, §421's `entry-missing`, §524's + `parts-differ`, §499's kept build), and `wait-ended` on `.wait-ended..` when the hold ends. The + keeper raises no condition of its own from them: the watchman writes `delivery-waits` for a `wait-held` of the + node-engine standing past composing's 30 minutes (§266), naming the node, the module and the reason, and it clears + on `wait-ended`; a `composed` whose fingerprint is not the want's is covered by that same wait or by the put's + wait (§498). - **The healers' attempts.** The seat `mesh-healers` says `attempt-changed` on `mesh.seat.mesh-healers.event.attempt-changed.`, the condition's key escaped as a key's name is (§73), carrying the attempt's value whole as its key in `attempts` holds it and `until`, the end of that key's lifetime, or `removed: true` when the healer ends the attempt; the seat's definition, in its contracts module, - keeps it last per subject, 7 days and 16 MiB (§396). The keeper's durable consumer starts from the last message per - subject, and the keeper drops an attempt at its `until` on its own clock. + keeps it last per subject, 7 days and 16 MiB (§396). The healers say it again for every open attempt at their start. The + keeper reads it as §556 has it, and drops an attempt at its `until` on its own clock. Why: the keeper reads no other module's state (ADR 0596 §2; develop question COND8). How it is checked: whole (keeper, healers, node-engine), an attempt said is shown on its condition and dropped at its `until` or its - removal, a node-engine's hold is heard from `wait-held`, and the keeper's manifest declares no `reads`. + removal, a node-engine's hold is heard from `wait-held` and past 30 minutes raises `delivery-waits`, a restarted keeper + holds the attempts it held, and the keeper's manifest declares no `reads`. ## Views of the catalog half, per watched branch @@ -263,14 +305,18 @@ line. Questions that an earlier line already answers have no line here: develop runner's own wiring of `catalog-revision.>` goes for it. - **The bus module across branches.** The bus module plans a module's kept data from the newest revision of each watched branch that lists the module: each local name once, from the first of the module's watched branches, in - its registration's order, whose revision lists it; another branch asking that local name with other settings is + its registration's order, whose revision lists it. A registration's watched branches are a list in that order: the + branch `register` names first (the repository's default branch, a placement's default, ADR 0666 §6), then each + branch in the order `watch` added it; `unwatch` removes one and keeps the others' order; another branch asking that local name with other settings is said as the bus module's `drifted`, naming the branch (ADR 0595 §6), and plans nothing. This amends §358, §365, §421, §433 and §507. Why: no part fixes a branch (§7, ADR 0666 §5), and a registration may add a watched branch while a part runs (develop questions NATS3, VW1 and COND6, and the client's lost per-branch events). How it is checked: unit (`go/views`), a view adopted after the start takes its first view and follows, and two branches' events reach their own views; whole (keeper, bus module), a branch registered while they run is - followed with no restart; the code review finds no branch's name in any part. + followed with no restart; whole (bus module), a feature branch asking a local name with other settings than the + default branch's is said `drifted` while the default branch's are planned; the code review finds no branch's name + in any part. ## The bus module's plans @@ -289,8 +335,10 @@ line. Questions that an earlier line already answers have no line here: develop a seat whose verbs come only from an included part gives its holder a sent store, and one including only bodies gives none. - §538 **The bus module plans again when the bus-state entries change.** The bus module compares, for each module, - the bus-state entries the catalog graph entry carries with those it last planned for that module, kept in its own - state, and plans again only when they differ; it never compares the manifest's digest (§506), which changes with + the bus-state entries the catalog graph entry carries with those it last planned for that module, kept in its old + `state` section `plans` (the bus module is a foundation part), one key `` holding the entries as planned + and their digest, written after each plan and deleted when the module is in no watched branch's newest revision, its + entries then orphans that `orphans` lists; its tool `nats_plans` reads it. It plans again only when they differ; it never compares the manifest's digest (§506), which changes with any field. Why: what it plans is those entries alone (develop question on §433 with §506). How it is checked: whole (bus module), a manifest changed outside `requires` plans nothing, and a changed option plans the difference. @@ -353,12 +401,17 @@ line. Questions that an earlier line already answers have no line here: develop lead-47's terminal: approval is a step whose status each module's registration sets, never a gate of its own, so an Approve given before G counts, once G is delivered, as that pull request's `approval` step and nothing else is special. At G's switch the gate says, for each `merges` key the old bridge had not performed, `approval-carried` on - `mesh.seat.mesh-delivery.event.approval-carried` (`repository`, `pull`, `commit` the head the Approve named, - `approver` the operator, `at`), declared in the seat's definition, and deletes the key once the bus acknowledges - it. The runner keeps each in its old `state` section `pipelines` as `carried-approval..`, - holding the commit, and takes it as the result `passed`, caller the operator, of the `approval` step of the first - pipeline or run at that commit, then deletes it; it deletes it unused when the seat `git` says the pull request - `pull-closed` or `pull-merged`. Where the module's policy makes `approval` `required`, a pull request whose head + `mesh.seat.mesh-delivery.event.approval-carried..` (`repository`, `pull`, `commit` the head the + Approve named, `approver` the operator, `at`), declared in the seat's definition with `keep` 90 days (§396). The gate + keeps its `merges` keys, read no more, until §97's removal, so a revert's gate of before G finds them. The runner + hears the event through a durable consumer made to start from the stream's first message, so a runner started + after the switch hears every one, and keeps each in its old `state` section `pipelines` as + `carried-approval..`, holding the commit. It takes it as the result `passed`, caller the + operator, only of the `approval` step of the delivering pipeline whose source revision takes in that pull + request's merge, as `take-in.taken`'s entries name it (each entry carrying the repository and the pull request of a + `merged` message, §493, §509), and only when the pull request's head merged is that commit; never in a run on + request and never in another pipeline at the same commit; then it deletes the key, and deletes it unused when the + seat `git` says the pull request `pull-closed`. Where the module's policy makes `approval` `required`, a pull request whose head moved after the Approve asks a new approval, as any step whose input changed does (an `approval` step is never carried, ADR 0666 §8); where it is `optional` or `bypass`, the carried Approve holds nothing. The merge itself is any merge (`merge-pull`, ADR 0666 §11): the bridge performs no carried act, the gate says no `wait-held` with @@ -367,8 +420,9 @@ line. Questions that an earlier line already answers have no line here: develop `cancelled`, `not asked`) or its pipeline is superseded or cancelled, so no approval wait stands without an end. This replaces §510 and §608, and `merge` leaves §498's list. Why: the operator's answer ("you're mixing in step and approval gates, this should be configurable"); the event, the key and the deletion are the design's (round 3, - health and events' R7). How it is checked: whole (gate, runner), a carried Approve of a head passes that head's - `approval` step with the operator as caller; with `approval` `required`, a head moved after the Approve asks a new + health and events' R7). How it is checked: whole (gate, runner), a carried Approve of a head passes the `approval` step + of the pipeline taking in that pull request's merge, with the operator as caller, and no run on request at that + commit; a runner started after the switch takes it all the same; with `approval` `required`, a head moved after the Approve asks a new approval; a pull request closed deletes its carried key; every `approval` wait-held in the whole test has its `wait-ended`; the code review finds no `merge-waits`. @@ -379,11 +433,13 @@ line. Questions that an earlier line already answers have no line here: develop ask under its name. The grants part grants each asker publish on `mesh.seat..accept..` (§600) for every queue of the seat on which a kind it is a caller of is taken, and the holder, which takes the asker from the subject (§481), refuses an ask whose body names a kind that asker is not a caller of, answering `error` naming - the kind and the asker, and ends it with `step-ended` `failed`. This amends §338 and §600. Why: §600 puts the queue + the kind and the asker, and ends it with `step-ended` `failed`. On main's subject `accept.`, which names no asker and is granted to + the checker and the builder alone (§600), the holder takes only the kinds `build` and `run` and refuses any other, + `step` among them, as above. This amends §338 and §600. Why: §600 puts the queue in the subject and the kind in the body, so a subject's grant alone cannot hold a caller to its kinds (round 3, security and upgrade, seams and tools: R1). How it is checked: unit (Go build module), a `step` published from the indexer's account is answered `error` naming `step` and `indexer`; unit (grants part), each asker's grants are the - queues of its kinds. + queues of its kinds; unit (Go, Node and image build modules), a `step` on main's subject is answered `error`. - §544 **Named asks have a worker of their own.** For each queue the grants part makes, beside main's worker `SEAT___worker` filtered to main's `mesh.seat..accept.` alone, a second worker `SEAT___named_worker` filtered to `mesh.seat..accept..*`; the two filters do not overlap, @@ -392,16 +448,23 @@ line. Questions that an earlier line already answers have no line here: develop part runs takes no named ask. This replaces §601's "until every node's holder of a build seat is of G the grants part's worker keeps main's one filter", and with it the edge from the seats' holders to the grants part, which no manifest fact gives: the order is the asker's `invokes` of `.accept.` (kind 6, from the seat's holders - to the asker, §601), and the grants, written for a catalog revision before any want of it (*A credential and a - composition before the want*), exist before the asker runs. A named ask waits on its worker while no holder of G + to the asker, §601), and the grants part comes before every asker as the acting part of the step `grant` on + `every` (§203): each entry of a revision waits for the grants part's new version to act for it (§266), so no asker + of G runs before a grants part of G has made its named worker and its grant. A holder of G pulls main's worker + alone until its membership names the named worker, which the grants part writes as it binds every consumer + (§386), and says meanwhile, in its journal and its read tool, `named worker not bound`. A named ask waits on its worker while no holder of G runs, said by `build-capacity-waits` after 60 minutes, which corrects §483's "waits on the work queue, whose - consumer of before G takes only main's subject". At a revert, which reverts the askers before the grants part (ADR - 0656 §4), the grants part of G, when it no longer hears any asker of G, purges the subjects - `accept..*` of the stream and deletes the named workers, saying each in its journal; an ask is transport, + consumer of before G takes only main's subject". At a revert, the grants part of G takes its view of the revert's catalog revision before + it is itself reverted, since it acts for every entry of it (§203); at the first revision in which no entry `invokes` + any `.accept.`, it purges the subjects `accept..*` of the stream and deletes the named workers, + within its granting bound of 10 minutes (§266), saying each in its journal. Where that did not run (the grants part + stopped first), its tool `grants_named_purge`, of class `act` with §391's default callers and written in the + hand-act log, does it by hand; an ask is transport, not kept data (as §59 has a sent object). Why: one consumer per filter, and a filter that never moves cannot be pulled by a holder that does not read it (round 3, R2, the security reviewer's form). How it is checked: whole (grants part, Go build module), a holder of before G started after the grants part of G takes main's asks and no - named one; a revert leaves no named worker and no named ask. + named one; a revert leaves no named worker and no named ask; a holder of G before its binding pulls main's worker + and says `named worker not bound`. - §545 **The tool runner's artifact declares the words it states.** The artifact entry of the tool runner's manifest takes `states`, the capability words (§488) it states; the manifest validator refuses a package artifact's `calls` word (§605) that no artifact of the revision `states`, naming the word, and the delivery plan's edge of §605 runs to @@ -410,32 +473,39 @@ line. Questions that an earlier line already answers have no line here: develop reads the stated words at its own start; no event is heard and nothing polls. This amends §605's "each time the tool runner of its node starts". Why: the words are stated only at run time, and the plan needs them in a manifest (round 3, seams and tools: R3). How it is checked: unit (manifest validator), a `calls` word nothing `states` - refused; unit (delivery plan), the edge to the module stating the word. + refused; unit (tool runner), the words it states are exactly its manifest's `states`; unit (delivery plan), the edge to the module stating the word. - §546 **When the builder forgets a node's toolchain.** The builder deletes its `toolchains-heard` key `.` (§604) when the node-engine seat's `running..` says `stopped` (§365) for the module whose catalog graph entry claims the build seat of that language (its `acts` rule `build` with `on` naming - it, §535), heard through the durable consumer it already has of that seat's `running`; never on a placement, which + it, §535), heard through its consumer of that seat's `running`, read as §556 has it; never on a placement, which is the seat `mesh-module-assigner`'s state. The state is an old `state` section of the builder, a foundation part, one key per node and language, about 12 keys of about 0.3 KiB each, capped at 1 MiB, so it adds no objects store to §607's count (§58 plans one only for a state of the template); `builder_toolchains` reads it and - `builder_toolchains_drop` deletes one key, with why, written in the hand-act log of the seat `mesh-hand-acts`; a + `builder_toolchains_drop`, of class `act` with §391's default callers (`operator`, `sessions`), deletes one key, + with why, written in the hand-act log of the seat `mesh-hand-acts`; a holder says the key again at its start and on each change (§492). This amends §604's "when the node leaves the catalog's placements". Why: the builder reads no other module's state (ADR 0596 §2; round 3, seams and tools: R4). How it is checked: whole (builder), a build module stopped on a node deletes that node's key, and the drop tool - deletes one key and logs it. -- §547 **The sum of the bus's caps is read against its limit.** The bus module reads the bus's storage limit from the - server's account information at its start and at each plan, sums the cap of every entry it plans (states, objects - stores, sent stores, streams, logs) and of every old section the catalog graph's entries declare, and shows both in - its `list` and its read tool. A plan whose sum passes the limit is refused, naming the sum and the limit, said as - its `refused` (ADR 0595 §6), whether or not the server reserves a cap at creation. When G's check measures that the - server reserves a cap when a store is made (§607), each `-objects` is planned with 16 MiB instead of 64 MiB - (about 2.4 GiB for 150 states), a state needing more naming `objects-max-mib`. This amends §607. Why: the limit is a + deletes one key and logs it; a drop by another caller is refused by the bus. +- §547 **The sum of the bus's caps is read against its limit.** Measured on 2026-11-11 against an embedded bus + (nats-server 2.11.17): an account's storage limit reserves each stream's maximum size when the stream is made (a + second stream of 64 MiB under an account limit of 100 MiB is refused, "insufficient storage resources"), and a + server's own limit does not. So each `-objects` is planned with 16 MiB (about 2.4 GiB for 150 states), a + state needing more naming `objects-max-mib`, and no later measure is waited for. The bus module reads the account's + and the server's storage limits at its start and at each plan, sums the cap of every entry it plans (states, objects + stores, sent stores, streams, logs) and of every old section the catalog graph's entries declare, and shows the sum and + both limits in its `list` and its read tool. A plan whose sum would pass the account's limit is refused, naming the + sum and the limit, said as its `refused` (ADR 0595 §6): the plan refused is the one that adds or raises a cap, and + the caps already made stay as they are. With no account limit (unlimited) nothing is refused and the sum is shown + against the server's limit. This amends §607's 64 MiB and its measure in G's check. Why: the limit is a fact of the server, read rather than written in the design, and a sum over it fails loudly (round 3, running and - testing: R5). How it is checked: whole (bus module), a fixture server with a small limit refuses a plan past it, - naming the sum; the sum and limit are in `list`. -- §548 **The forge module writes `seen` only from the runner's steps.** §487's `seen..` is written - only from a `step-ended` whose asker is `runner`; the `step-ended` of any other ask (§602) carries `asker`, `ask`, - `node`, `outcome` and `at` and nothing of a pipeline, and the forge module writes nothing from it. This amends §487. + testing: R5). How it is checked: whole (bus module), a fixture bus with a small account limit refuses the plan + that would pass it, naming the sum, and keeps the caps made; with no account limit nothing is refused; the sum and + the limits are in `list`. +- §548 **The forge module writes `seen` only from a pipeline's steps.** §487's `seen..` is written only + from a `step-ended` that carries a pipeline's `commit` and `fingerprint`, which a pipeline's step carries; the + `step-ended` of any other ask (§602) carries `asker`, `ask`, `node`, `outcome` and `at` and neither, so the forge + module writes nothing from it and names no asker. This amends §487. Why: since §602 every ask says `step-ended`, and a builder's build is no run of a commit (round 3, health and events: R6). How it is checked: unit (forge module), a builder's failed build writes no `seen`; whole (watchman), a `stuck` raised for an ask clears on its late `step-ended`. @@ -454,16 +524,17 @@ line. Questions that an earlier line already answers have no line here: develop ## The operator's answers of 12:41 CET - §551 **A restore point is removed after 90 days, never the newest.** The operator, 2026-11-11 12:41 CET at lead-47's - terminal. The bus module removes a restore point's directory (§516) 90 days after its `at`, as long as delivery + terminal. The recovery module removes a restore point's directory (§516) 90 days after its `at`, as long as delivery records are kept, unless it is the newest point of its module and kept data, which it never removes; it checks at - its start, once a day and at each `snapshot-kept`, on its own clock and its own directory. Each removal is said as - the seat `mesh-broker`'s event `point-removed` (`module`, `kept`, `form`, `fingerprint`, `snapshot`, `at`), declared - in its definition; the backup holder consumes it and deletes that point's key in `bus-points` (§517), and its - repository's copy goes by the repository's own rotation (ADR 0235 §4). Data set aside is removed only by - `discard-aside` (§516). This completes §516 and answers issue 1921's note on the retention of restore points. Why: - the operator's answer; the event, the checks and the holder's deletion are the design's. How it is checked: whole - (bus module, backup holder), a point older than 90 days is removed and said and its key deleted, and the newest - point of a kept data, older than 90 days, stays. + its start, once a day and at each `snapshot-kept`, on its own clock and its own directory, as one of its acts, never + beside a `restore-kept` (§554). Each removal is written in the hand-act log of the seat `mesh-hand-acts` and said as + `mesh-recovery`'s event `point-removed` on `mesh.seat.mesh-recovery.event.point-removed..` (`module`, + `kept`, `form`, `fingerprint`, `snapshot`, `at`), kept 90 days (§396). The backup holder's copies go by its + repository's own rotation (ADR 0235 §4). Data set aside is removed only by `discard-aside` (§516). This completes + §516 and answers issue 1921's note on the retention of restore points. Why: the operator's answer; the event, the + checks and their order are the design's. How it is checked: whole (recovery module), a point older than 90 days is + removed and said, the newest point of a kept data older than 90 days stays, and a removal due during a restore runs + after it. - §552 **The checker may drop its own asks until its removal.** The operator, 2026-11-11 12:41 CET at lead-47's terminal: the build seats' verb `drop` (§338) gains the checker as a caller until its entry is removed with its buckets and bus account (ADR 0667 §12), as `run` has it, so the validator accepts the checker's manifest's 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 new file mode 100644 index 00000000..c57a01fc --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md @@ -0,0 +1,128 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-11 +decisions: + - "ADR 0667" + - "ADR 0666" + - "ADR 0660" + - "ADR 0656" + - "ADR 0636" + - "ADR 0626" + - "ADR 0619" + - "ADR 0618" + - "ADR 0614" + - "ADR 0596" + - "ADR 0595" + - "ADR 0564" + - "ADR 0505" + - "ADR 0404" + - "ADR 0363" + - "ADR 0240" + - "ADR 0235" +--- + +# 47, milestone G: the recovery module, and the review of §516 to §553 + +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". + +*§554 and §555 are the operator's answer of 2026-11-11 at 12:55 CET at lead-47's terminal. §556 to §558 answer the +four reviewers' round 1 of [G-12](G-12-develop-answers-fourth.md) (§516 to §553); the rest of that round's findings +are written into the lines of G-12 they name.* + +## The recovery module + +- §554 **The recovery module takes, sets aside and restores kept data on the live bus.** The operator, 2026-11-11 + 12:55 CET at lead-47's terminal: a module of its own, on a seat of its own, with an account of its own, owns + snapshotting, setting aside and restoring a module's kept data on the live bus, each act in the hand-act log; the + bus module keeps no restore permission (ADR 0626 §1 stays as it is), the snapshot account stays limited to the + nightly dump (ADR 0626 §2), and the backup holder only stores copies. The record of the recovery module, which + lead-47 proposes to the knowledge base for the operator's Approve, decides the account and its grants; this line + is written to it. + - **Its seat and module.** 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 keeps no kept data on the bus, its one store being its data directory (§516), so it is no foundation + part (ADR 0619 §1) and waits on no bus module. + - **Its verbs and callers.** `snapshot-kept` and `restore-kept` (with `aside-only`), class `act`, are called by the + node agent applying a want (`seat:node-engine`, §296, §297) alone; `restore-kept` with `discard-aside` by + `operator` alone; `kept-points` (§517), class `read`, takes §391's default. Each act is written in the hand-act + log of the seat `mesh-hand-acts` first, with the caller the bus proved and `why`; an act whose log write fails + does nothing and answers `error`, naming the log (as §480). + - **Its account.** Composed by the grants part from the seat's definition as the record decides: the bus's stream + snapshot and restore, a stream's deletion and a consumer's creation, only for the bus objects of a module's kept + data (the names a binding or an old section of a catalog graph entry gives), never for the mesh's own streams nor + for any other object; it publishes only on its own seat's subjects and reads no module's data otherwise. + - **Its events.** On `mesh.seat.mesh-recovery.event.`: `ended..` (a point taken, with `outcome` `done` + or `failed` and its `error`), `restored..` (likewise), `set-aside..` (with the aside's + `id` and `size`) and `point-removed..` (§551), each filled as §523 has it and kept 90 days (§396). + The node agent's wait for `ended` or `restored` is bounded at 90 minutes (30 for a nightly dump it waits behind, + 30 for a point brought back from the backup, 30 for the restore), past which its have says the wait, naming the + seat, and the watchman's `delivery-waits` raises it. This amends §405's 30 minutes. + - **One act at a time, and the nightly dump.** The recovery module runs its acts (`snapshot-kept`, `restore-kept` + and a removal of §551) one at a time, the next waiting within its own bound. The holder of `node-backup` says + `item-started..` and `item-ended..` for every item it backs up, declared in its seat's + definition; the recovery module consumes those of the bus module's item and starts no `restore-kept` while the + nightly dump of the bus runs, so the dump never meets a stream a restore removed (ADR 0235 §13). A `snapshot-kept` + may run beside the dump, both paced as ADR 0235 §10 has it, so the server has at most two snapshots at once. + - **Its order and its tools.** The node-engine's `invokes` of `mesh-recovery`'s verbs gives the served-verb edge + (kind 6) from the recovery module to the node-engine, so §298's "the new versions of the `node-backup` holder and + of the bus module run before any module whose kept data's form changes is moved" reads "the new version of the + recovery module". Its tools are named `recovery_`, and its declared health check calls `kept-points`. + + Why: the operator's answer; the seat's placement in mesh-contracts, its callers, its events, its bound and its + order are the design's, from §296 to §303 and §405 as they were. How it is checked: test 36 with the recovery + module; unit (grants part), the recovery module's account holds the grants the record names and no other, and the + bus module's and the snapshot account's hold no restore; whole (recovery module), a `restore-kept` asked while the + bus's item is backed up starts after `item-ended`; an act whose hand-act log write fails does nothing. +- §555 **What moves to the recovery module.** §296 asks `mesh-recovery`'s `snapshot-kept` and waits for its `ended`, + never the seat `node-backup`; §297 asks its `restore-kept` and waits for its `restored`; §302's `backed-up` lists + the recovery module's data item as any item, and the restore points and asides are listed by `kept-points` (§517), + `discard-aside` being `restore-kept`'s; §303 and test 36 name the recovery module where they named the backup + holder and the bus module; §337's and §405's callers `holder:node-backup@same-node` go with the two verbs of + `mesh-broker`, which gains none; the seat `node-backup` gains nothing for kept data on the bus: its `aside` and + `discard-aside`, the arguments `kept`, `form` and `fingerprint` of its `now` and `restore`, and its events + `ended...` and `restored...` go from its definition in mesh-contracts, and + it gains only `item-started` and `item-ended` (§554); §450's `aside` answer is `restore-kept`'s with `aside-only`; + §406's catch-up is recorded in the point as before. Why: one owner of the acts (§554). How it is checked: unit + (definitions), `node-backup` and `mesh-broker` hold none of the moved verbs; test 36. + +## The review of §516 to §553, round 1 + +- §556 **A part that follows a stream kept last per subject reads it whole at each start.** A durable consumer goes on + from its acknowledged position after a restart, so a part that acts on what it heard from a stream kept last per + subject, and keeps no state of it, reads at each of its starts every subject's last message through an ordered + consumer it makes for that one read, then follows its durable consumer from its acknowledged position, an event + older by `at` than what that read gave changing nothing. Until the read ends the part acts on none of those events + and its `_first_views` names the stream `waiting`. So read: the node-engine seat's `running`, `composed`, + `wait-held` and `wait-ended` (the last two kept on the stream `running` is kept on, last per subject, a node's + module's newer one by `at` standing), by the assigner (§520), the condition keeper (§541), the builder (§546), the + node-engine and the grants part (§199); and `mesh-healers`' `attempt-changed` by the keeper (§541). This amends + §199's "each part's durable consumer starts from the last message per subject". Why: what a part heard must not + depend on when it last restarted (review round 1, health and events 2, with running and testing 3). How it is + checked: whole (assigner, keeper), a restarted assigner holds no want it did not hold before, and a restarted + keeper holds the healers' attempts it held. +- §557 **A whole test's bounds are settings.** Every bound a whole test waits on (§535's five minutes, §534's 30 + minutes, §544's 60 minutes, §540's two hours, §551's 90 days, §554's 90 minutes) is a setting of the part that + times it, read at its start, its default the design's value, so a whole test sets it in seconds and never waits on + real time. The bus module's and the recovery module's whole cases in these lines are cases of each module's one + whole test against the real bus, within the test budget. Why: a whole test that waits hours runs never (review + round 1, running and testing's notes). How it is checked: the code review; each whole test of these lines runs in + under a minute. +- §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 + 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 + consumer, held until the holder runs a build whose catalog graph entry's seat definition (§194) gives the verb's + answer every named field; §374's entry of kind 6 records the verb and its fields, and `outdated` (§375) says it + behind when the delivered version no longer gives them. The grants part's manifest names `mesh-broker`'s `list` + with `fields: [binding]`, and every part of G relying on a field G adds to a served answer names it so in G's + change. 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. diff --git a/03-DESIGN/01-to-be/47-milestones/G-4-revision-view-and-steps.md b/03-DESIGN/01-to-be/47-milestones/G-4-revision-view-and-steps.md index 6ce9f057..f69a6cd9 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-4-revision-view-and-steps.md +++ b/03-DESIGN/01-to-be/47-milestones/G-4-revision-view-and-steps.md @@ -105,7 +105,7 @@ One file of milestone G's part of [design 47](../47-delivery-from-commit-to-deli asked it (1921's note of 2026-11-07: "the indexer reads its seat definitions at start"). How it is checked: in the indexer's whole-module test, started with a definition on a contracts trunk that no event announced, the first manifest claiming that seat is judged with it. -- §199 **Who holds a seat is applied by one rule, from what each node runs.** The node-engine and the grants part decide a +- §199 **Who holds a seat is applied by one rule, from what each node runs.** (§556) The node-engine and the grants part decide a seat's pass by mesh-sdk's `SeatsToPassOn` (ADR 0660 §3), reading the catalog graph's entry of the build each assignment runs, never a manifest (ADR 0660 §2; §442). What each node runs is heard, never asked: each part keeps it from the node-engine seat's events `running..` (*What each step says*), through a durable consumer of its own declared in its `consumes`, diff --git a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md index 88526d31..f5ded827 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md +++ b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md @@ -329,7 +329,7 @@ later change of a kept data's form, not as a case of this milestone. changes how it writes that data, or stops declaring it; a declaration naming none reads as form 0. The declaration alone cannot tell a changed layout of the same bucket, so the holder says it. Only kept data whose `form` changes is backed up and restored, so a part's other kept data, and every decision it holds, is never set back. -- §296 **It is backed up before the module moves.** When a want moves a module whose new manifest declares a kept data at +- §296 **It is backed up before the module moves.** (§555) When a want moves a module whose new manifest declares a kept data at another `form` than the running one, or no longer declares it, the node agent applying it first asks the seat `node-backup` of the node holding `mesh-broker` for a restore point of that kept data, and waits for its event `ended`, as for any backup before the apply. The bus module's account takes it, as it takes the bus's snapshots @@ -337,14 +337,14 @@ later change of a kept data's form, not as a case of this milestone. before, and it holds the kept data whole, the position of each durable consumer the module reads with, and the catch-up tool the newer manifest declares beside that kept data. A failed backup holds that module as any failed backup does. -- §297 **A revert restores it before the reverted build starts.** When a want takes a module back across a change of a +- §297 **A revert restores it before the reverted build starts.** (§555) When a want takes a module back across a change of a kept data's `form`, the node agent applying it stops the newer build, then asks that seat's verb `restore` (§405) for the restore point of that module, kept data and form, waits for the seat's event `restored`, and only then starts the reverted build. The node agent that restores is the one running when the revert starts. As every restore is, it is brought back beside the live data and never over it: the live kept data of the newer form is set aside under a name of its own, and only the operator removes it, so nothing written since is destroyed. The consumer's position is set back with it, so the reverted build hears everything since as after any restart. -- §298 **The order of a delivery that crosses a form change** is the module graph's (*Order without a person*), with three +- §298 **The order of a delivery that crosses a form change** (§554) is the module graph's (*Order without a person*), with three edges the assigner, which writes every want, keeps: - the new versions of the `node-backup` holder and of the bus module run before any module whose kept data's form changes is moved, since the restore point needs both; @@ -391,12 +391,12 @@ later change of a kept data's form, not as a case of this milestone. (for milestone G, its delivery records and its graph revisions) is set aside and not read again; an Approve asked and answered since is asked again, and one the gate asked before still reads its answer. The revert's Approve ask says this in those words, naming each kept data restored. -- §302 **Its tools.** (§517) The seat `node-backup`'s `backed-up` lists each module's restore points of its bus data, with the +- §302 **Its tools.** (§517, §555) The seat `node-backup`'s `backed-up` lists each module's restore points of its bus data, with the kept data, the form and the fingerprint each was taken before, and the data set aside at each restore, with its size; `restore` restores one; `discard-aside` removes data set aside by a restore, only on the operator's call, and writes the act, who and why in the hand-act log of the seat `mesh-hand-acts`. `backed-up` and `restore` exist on the seat today and gain the module's kept data on the bus as an item; `discard-aside` is new. -- §303 **How it is checked.** Whole (node-engine, backup holder, bus module): a want moving a module across a `form` +- §303 **How it is checked.** (§555) Whole (node-engine, backup holder, bus module): a want moving a module across a `form` change, and one moving a module whose kept data declares no `form` to one that does, each take a restore point first, keyed by module, kept data, form and fingerprint; a want taking it back restores it beside the live data and the consumer's position before the reverted build starts, and the data set aside is listed with its size and diff --git a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md index 0ebd9f32..6a3342b9 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -161,7 +161,7 @@ record it follows from, and replaces the builder's own choice where one was made definitions in `mesh-connectivity-contracts` names `invokers`, `persons` and `sessions` in this milestone, since sessions and persons call them today through the mesh MCP server. How it is checked: unit (definition reader), each form accepted and any other entry refused; the grants part's table test grants each form. -- §337 **The bus module's restore points.** The verbs the holder of `node-backup` on the bus module's node calls to take +- §337 **The bus module's restore points.** (§555) The verbs the holder of `node-backup` on the bus module's node calls to take and restore a module's kept data on the bus name `holder:node-backup@same-node`. Why: only that holder takes them (*A backup before the apply: kept data on the bus*). How it is checked: a call from another node's `node-backup` holder is refused by the bus. @@ -406,7 +406,7 @@ record it follows from, and replaces the builder's own choice where one was made at G's delivery (design 51, *Tagged versions*). Why: the operator, 2026-11-10 about 21:40 CET, no custom versions for now, a tagged version being one; a run delivers nothing (ADR 0666 §18), so it needs no version; the digest's source and the wait are the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: test 103. -- §374 **A release records, per edge, the part it was built against or delivered after.** One version per module +- §374 **A release records, per edge, the part it was built against or delivered after.** (§558) One version per module stays (§52), and an edge is judged by the digest of the part it points to. The catalog records on each released version (§349), in its key of `versions`, one entry per catalog-graph edge of the module's artifacts (the six kinds of *Dependencies between artifacts*), a lockfile per released version. An entry names the kind, whether the edge diff --git a/03-DESIGN/01-to-be/47-milestones/G-9-prepare-answers.md b/03-DESIGN/01-to-be/47-milestones/G-9-prepare-answers.md index b24b1345..8f006242 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-9-prepare-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-9-prepare-answers.md @@ -116,7 +116,7 @@ line says so.* ### Kept data on the bus, restore points and the reader release -- §405 **The bus module's restore points and node-backup's verbs.** (§516, §517) The seat `mesh-broker` gains `snapshot-kept` and +- §405 **The bus module's restore points and node-backup's verbs.** (§516, §517, §554, §555) The seat `mesh-broker` gains `snapshot-kept` and `restore-kept`, of class `act`, their callers `holder:node-backup@same-node` alone (§337): the bus module's account is the one that moves bus data, so `restore-kept` sets the live kept data aside under a name of its own and then restores the restore point beside it, with the argument `aside-only` it sets the live data aside and restores @@ -132,7 +132,7 @@ line says so.* choice, one answer to three preparers (Q8, S9, Q-H6, C19; lead-47's ruling, 2026-11-11). How it is checked: test 36; a call of `snapshot-kept` from another node's `node-backup` holder, and a `discard-aside` from any caller but the operator, are refused by the bus; a failed restore says `restored` with `failed` and its error. -- §406 **A catch-up names its tool and arguments.** (§516) A restore point records the catch-up tool and its arguments whole +- §406 **A catch-up names its tool and arguments.** (§516, §555) A restore point records the catch-up tool and its arguments whole (§299), and the fingerprint of the newer build it was taken before; the grants part derives the grant of that tool to `holder:node-engine@same-node` from the catalog graph's entry of that recorded fingerprint (§443). Why: the design's choice (Q-H7). How it is checked: whole (node-engine), a restore runs its recorded catch-up, granted from the @@ -380,10 +380,10 @@ line says so.* - §449 **The grants part's fill events say what `mesh-wants`' say.** The seat `mesh-grants`' events `filling` and `room` carry the same body as `mesh-wants`' fill events: `store`, `bytes`, `cap` and `at` (§181, §331). Why: develop question D-4, 2026-11-11. How it is checked: the seat's fixtures, against `mesh-wants`'. -- §450 **`ask-again` and `aside` answer.** `operator-channel`'s `ask-again` (§394) takes `key`, the ask's, and answers +- §450 **`ask-again` and `aside` answer.** (§555) `operator-channel`'s `ask-again` (§394) takes `key`, the ask's, and answers `state`, the ask's state after it, or `error`; `node-backup`'s `aside` (§405) answers `aside`, the name the data was set aside under, or `error`. Why: develop question D-5, 2026-11-11. How it is checked: the seats' fixtures. -- §451 **The forms of `owner`, `waiting-on` and a catch-up.** A finding's `owner` and `waiting-on` (§434, §439) are each +- §451 **The forms of `owner`, `waiting-on` and a catch-up.** (§540) A finding's `owner` and `waiting-on` (§434, §439) are each a string: a person or a session as who@node, or a seat's name. A restore point's catch-up (§406) is `{tool, arguments}`: `tool` an address as the mesh MCP server names one, `arguments` an object. Why: develop question D-6, 2026-11-11. How it is checked: unit (mesh-conditions' contract, node-backup's contract), each form read and another diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index 3e8bd514..f96ae217 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -23,6 +23,7 @@ decisions: - "ADR 0641" - "ADR 0639" - "ADR 0636" + - "ADR 0626" - "ADR 0635" - "ADR 0633" - "ADR 0625" @@ -76,7 +77,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 thirteen files, each read whole, its design lines numbered across them: +The part is fourteen 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. @@ -91,3 +92,4 @@ The part is thirteen files, each read whole, its design lines numbered across th - [G-11-develop-answers-review](G-11-develop-answers-review.md): the operator's answers of 2026-11-11 noon, the review of §456 to §479, and develop's next questions (§480 to §515). - [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 §558). -- 2.54.0 From a3d9975e4b1e1c9ef1b18be49018e53a03aa6546 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 13:06:14 +0100 Subject: [PATCH 5/9] Answer the last review round of batch 4: the recovery module's acts, order, callers and grants Its verbs answer on acceptance and its events carry the act's form and fingerprint, it never restores beside the nightly dump either way, a node-engine may call it only for a want its node holds, and its grants are listed whole as ADR 0672 has them; the long form of an invokes entry is read by the second reader release first. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../G-12-develop-answers-fourth.md | 35 ++-- .../G-14-recovery-module-and-review.md | 156 ++++++++++++------ 03-DESIGN/01-to-be/47-milestones/G.md | 2 + 3 files changed, 132 insertions(+), 61 deletions(-) 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 e597b32c..42c49686 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,7 @@ design: 47 milestone: G updated: 2026-11-11 decisions: + - "ADR 0672" - "ADR 0667" - "ADR 0666" - "ADR 0660" @@ -43,17 +44,20 @@ that round are written into the lines they name, here. Questions that an earlier - §516 **A restore point's bytes, and the forms of `snapshot-kept` and `restore-kept`.** (§551, §554) The seat `mesh-recovery`'s two verbs (§554) take and answer the forms mesh-contracts' definition gave the seat `mesh-broker`'s, which move to `mesh-recovery` and which this line makes the design's: `snapshot-kept` takes `module`, `kept` (the - kept data's local name), `form`, `fingerprint`, `why` and `catch-up` as `{tool, arguments}` (§451), and answers - `point` (the module, the kept data, its form, the fingerprint, `snapshot`, `consumers` with each durable consumer's - stream, name and acknowledged sequence, `size` in bytes, `at` and `catch-up`) or `error` alone; `restore-kept` takes + kept data's local name), `form`, `fingerprint`, `why` and `catch-up` as `{tool, arguments}` (§451); its point + (the module, the kept data, its form, the fingerprint, `snapshot`, `consumers` with each durable consumer's + stream, name and acknowledged sequence, `size` in bytes, `at` and `catch-up`) comes on its event `ended` (§554); `restore-kept` takes `module`, `kept`, `form`, `fingerprint`, `why`, `from-backup` (yes or no) and one of `snapshot` (the restore point's - name), `aside-only` or `discard-aside` (an aside's `id`), and answers `restored`, `aside`, `missing` (true when the - point is not in the recovery module's directory) and `error`. No argument `local-name` or `from` is taken, and a + name), `aside-only` or `discard-aside` (an aside's `id`), and its result (`restored`, `aside`, `missing`, true when the + point is not in the recovery module's directory, and `error`) comes on its event `restored`. Both verbs answer only + `accepted`, `busy` or `error` (§554). No argument `local-name` or `from` is taken, and a catch-up is never text. - **Where a point lives.** The recovery module takes a point through the bus's stream snapshot under its own account (§554), one bus object of the kept data at a time (its stream, or the stream under its bucket or object store), - with ADR 0235 §10's pacing and §11's bounds, and writes it with the point's record in its own data directory, - `points///-`, each token escaped as a key's name is (§73) and a token that is + with ADR 0235 §10's pacing and §11's bounds, and writes it with the point's record under a temporary name, renamed to its own name only once the point is + whole (as ADR 0235 §2 writes the dump), in its own data directory, `points///-`; + at its start it removes every point left under a temporary name, so a point cut off by a restart, the bus away or + its bound is never listed nor restored; each token escaped as a key's name is (§73) and a token that is empty, `.` or `..` refused; that name is the point's `snapshot`. - **Its copy.** That directory is an item of the recovery module's manifest `data`, backed up by the holder of `node-backup` on its node as every item is (ADR 0404), which stores the copy and nothing else. When @@ -62,15 +66,16 @@ that round are written into the lines they name, here. Questions that an earlier 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. - **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 (`at` the UTC time written - `20261111T121200Z`, with `-2`, `-3` and on added when that name exists), only then removes the live objects, + 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 (whether that removal needs a person's warrant waits for the operator, ADR 0672 + review K3), restores the point under their names and sets each consumer to its recorded sequence. Each step is written first in the aside's record (`set aside`, `removed`, `restored`): when a step after the removal fails, `restore-kept` restores the aside under the live names and sets the consumers back before it answers `failed`, and at its start the recovery module does the same for every aside whose record ends at `removed`, so a failed or stopped restore never leaves the kept data gone. The aside's `id` is that directory's name, its `name` the bus name it was live under, its `size` its bytes on disk; nothing of an aside stays on the bus. `discard-aside` removes that directory - and nothing else. A restore point is removed only as §551 says. + and nothing else, an `id` holding `.`, `..` or `/` refused. A restore point is removed only as §551 says. This completes §405 and §406. Why: the operator's answer of 12:55 CET (§554) puts the taking and restoring of a module's kept data on the live bus on a module of its own; the server restores a stream only where no stream of that @@ -80,10 +85,10 @@ that round are written into the lines they name, here. Questions that an earlier module, backup holder, bus), a point is taken, its directory removed, brought back with `from-backup` and restored, the live data's aside listed with its size and the consumer at its recorded sequence; a point missing from both says `restore point missing`; a restore made to fail after the removal, and one stopped there and started again, leave - the live data as before. + the live data as before; a point cut off half way is removed at start and never listed. - §517 **The recovery module lists what it holds.** The seat `mesh-recovery`'s read verb `kept-points` (class `read`, §391's default callers) lists every restore point and aside in its directory with its module, kept data, form, - fingerprint, size and `at`, and its tools are `recovery_kept_points`, which reads the same, and + fingerprint, size and `at`, and the act that runs, with since when and what it waits on (§554); its tools are `recovery_kept_points`, which reads the same, and `recovery_expire_points`, which runs §551's removal now (class `act`, §391's default callers, written in the hand-act log). The directory is the one place they are kept, so no copy can list less: the backup holder keeps no state of them (its `backed-up` lists the recovery module's item as any item, §302), and no `bus-points` state is @@ -120,7 +125,8 @@ that round are written into the lines they name, here. Questions that an earlier answers each with `{seq}` or `{error}`, the bundle sends the next only after a `{seq}`, and the answer to the last is the put's answer; a `seq` that is not one more than the last taken is answered `{error}` naming both, and a put not sealed within the call's bound is dropped (§345). The tool runner writes each chunk into the store as it takes it, through the - object store's own chunking, holding no more than one chunk in memory, and seals the object on the last. + object store's own chunking, holding no more than one chunk in memory, and seals the object on the last. A put dropped at its bound has its written chunks purged by the tool runner, + which deletes the unsealed object; until then they count against the store's cap. - **Get.** The tool runner answers the get call `{chunked: , size, digest, call}` (§490), and the bundle asks each chunk in order by the call `mesh/objects.get-chunk` with `call` and `seq`, answered `{seq, last, data}` or `{error}`; the bundle checks the whole against `digest`, and a get not completed within the call's bound fails, @@ -435,7 +441,8 @@ that round are written into the lines they name, here. Questions that an earlier the subject (§481), refuses an ask whose body names a kind that asker is not a caller of, answering `error` naming the kind and the asker, and ends it with `step-ended` `failed`. On main's subject `accept.`, which names no asker and is granted to the checker and the builder alone (§600), the holder takes only the kinds `build` and `run` and refuses any other, - `step` among them, as above. This amends §338 and §600. Why: §600 puts the queue + `step` among them, as above; the builder's `run` there is taken until the checker's removal (§97), after which + main's subject goes (§483). This amends §338 and §600. Why: §600 puts the queue in the subject and the kind in the body, so a subject's grant alone cannot hold a caller to its kinds (round 3, security and upgrade, seams and tools: R1). How it is checked: unit (Go build module), a `step` published from the indexer's account is answered `error` naming `step` and `indexer`; unit (grants part), each asker's grants are the 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 c57a01fc..f7fe2038 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,8 @@ design: 47 milestone: G updated: 2026-11-11 decisions: + - "ADR 0672" + - "ADR 0436" - "ADR 0667" - "ADR 0666" - "ADR 0660" @@ -35,48 +37,95 @@ are written into the lines of G-12 they name.* ## The recovery module -- §554 **The recovery module takes, sets aside and restores kept data on the live bus.** The operator, 2026-11-11 - 12:55 CET at lead-47's terminal: a module of its own, on a seat of its own, with an account of its own, owns - snapshotting, setting aside and restoring a module's kept data on the live bus, each act in the hand-act log; the - bus module keeps no restore permission (ADR 0626 §1 stays as it is), the snapshot account stays limited to the - nightly dump (ADR 0626 §2), and the backup holder only stores copies. The record of the recovery module, which - lead-47 proposes to the knowledge base for the operator's Approve, decides the account and its grants; this line - is written to it. - - **Its seat and module.** 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 keeps no kept data on the bus, its one store being its data directory (§516), so it is no foundation - part (ADR 0619 §1) and waits on no bus module. - - **Its verbs and callers.** `snapshot-kept` and `restore-kept` (with `aside-only`), class `act`, are called by the - node agent applying a want (`seat:node-engine`, §296, §297) alone; `restore-kept` with `discard-aside` by - `operator` alone; `kept-points` (§517), class `read`, takes §391's default. Each act is written in the hand-act - log of the seat `mesh-hand-acts` first, with the caller the bus proved and `why`; an act whose log write fails - does nothing and answers `error`, naming the log (as §480). - - **Its account.** Composed by the grants part from the seat's definition as the record decides: the bus's stream - snapshot and restore, a stream's deletion and a consumer's creation, only for the bus objects of a module's kept - data (the names a binding or an old section of a catalog graph entry gives), never for the mesh's own streams nor - for any other object; it publishes only on its own seat's subjects and reads no module's data otherwise. - - **Its events.** On `mesh.seat.mesh-recovery.event.`: `ended..` (a point taken, with `outcome` `done` - or `failed` and its `error`), `restored..` (likewise), `set-aside..` (with the aside's - `id` and `size`) and `point-removed..` (§551), each filled as §523 has it and kept 90 days (§396). - The node agent's wait for `ended` or `restored` is bounded at 90 minutes (30 for a nightly dump it waits behind, - 30 for a point brought back from the backup, 30 for the restore), past which its have says the wait, naming the - seat, and the watchman's `delivery-waits` raises it. This amends §405's 30 minutes. - - **One act at a time, and the nightly dump.** The recovery module runs its acts (`snapshot-kept`, `restore-kept` - and a removal of §551) one at a time, the next waiting within its own bound. The holder of `node-backup` says - `item-started..` and `item-ended..` for every item it backs up, declared in its seat's - definition; the recovery module consumes those of the bus module's item and starts no `restore-kept` while the - nightly dump of the bus runs, so the dump never meets a stream a restore removed (ADR 0235 §13). A `snapshot-kept` - may run beside the dump, both paced as ADR 0235 §10 has it, so the server has at most two snapshots at once. - - **Its order and its tools.** The node-engine's `invokes` of `mesh-recovery`'s verbs gives the served-verb edge - (kind 6) from the recovery module to the node-engine, so §298's "the new versions of the `node-backup` holder and - of the bus module run before any module whose kept data's form changes is moved" reads "the new version of the - recovery module". Its tools are named `recovery_`, and its declared health check calls `kept-points`. +- §554 **The recovery module takes, sets aside and restores kept data on the live bus.** The operator, + 2026-11-11 12:55 and 13:02 CET at lead-47's terminal: a module of its own, on a seat of its own, with an account of + its own, owns snapshotting, setting aside and restoring a module's kept data on the live bus, each act in the + hand-act log; the bus module keeps no restore permission (ADR 0626 §1 stays as it is), the snapshot account stays + limited to the nightly dump (ADR 0626 §2), and the backup holder only stores copies. ADR 0672 decides the account, + its grants and its callers; this line is written to it. + - **What it alone does.** It alone takes a restore point before a module moves across a `form` change, and alone + restores into the live bus. The bus module's snapshot before its own `recreate` or `drop` (ADR 0595 §5, ADR 0626 + §4) and the nightly dump (ADR 0626 §2) stay theirs. The restore ADR 0667 §12 tests of the snapshot `drop` takes is + the recovery module's `recovery_restore` naming that snapshot, which it reads through the bus module's tool + `nats_snapshot_get` (class `read`, callers `holder:mesh-recovery@same-node` alone). + - **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 + 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 + 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. + - **Who calls it.** `snapshot-kept` and `restore-kept` are called by the node-engine holder of the node that holds + the want moving that module (`seat:node-engine`, proved by the bus as that node's account): the recovery module, + which hears `mesh-wants`' `wanted` as §556 reads a stream, answers `error` naming the node unless that node's want + names the module at `fingerprint` (for `snapshot-kept`) or moves it back from `fingerprint` (for `restore-kept`). + `kept-points` takes the default of a read (§391). `aside-only` and `discard-aside` wait for the operator (ADR 0672 + review, K3 and K5). Each act is written in the hand-act log of the seat `mesh-hand-acts` first, with the caller the + bus proved and `why`; an act whose log write fails does nothing and answers `error`, naming the log (as §480). + - **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 skip the want check, since the + operator's call is the reason. + - **Its account.** Composed by the grants part from the seat's definition and the manifest: 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` and of `mesh-wants`' `wanted`, each with the ordered + consumer §556 needs; 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`, + `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 + the fingerprint it acts at, read by `mesh-catalog`'s `entry` (§443) in its `invokes` (an edge of kind 6): a + template entry's name is `_` (ADR 0595 §1), an old section's its declared name; a point records the + names it took, and a restore uses those. + - **Never beside the nightly dump.** A `snapshot-kept` may run while the dump runs, both paced as ADR 0235 §10 has + 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 + 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 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 + `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_`. - Why: the operator's answer; the seat's placement in mesh-contracts, its callers, its events, its bound and its - order are the design's, from §296 to §303 and §405 as they were. How it is checked: test 36 with the recovery - module; unit (grants part), the recovery module's account holds the grants the record names and no other, and the - bus module's and the snapshot account's hold no restore; whole (recovery module), a `restore-kept` asked while the - bus's item is backed up starts after `item-ended`; an act whose hand-act log write fails does nothing. + Why: the operator's answers; the node, cap, events, answer on acceptance, dump order, health and edges are the + design's (review rounds 1 and 2; ADR 0672). How it is checked: test 36 with the real backup holder and the recovery + module; unit (grants part), the recovery module's account holds exactly the grants above, a call outside its names + refused, both a point's old-section names and a template's binding granted, and the bus module's and the snapshot + account's hold no restore; whole (recovery module), a call from a node holding no such want answered `error`, a + `busy` answer then the act on the other's `ended`, a second form change not woken by the first's `ended`, a dump + asked during a restore starting after `restored`, a restart during the dump starting no restore, an act whose + hand-act log write fails doing nothing, and an unrestored aside failing the health check. - §555 **What moves to the recovery module.** §296 asks `mesh-recovery`'s `snapshot-kept` and waits for its `ended`, never the seat `node-backup`; §297 asks its `restore-kept` and waits for its `restored`; §302's `backed-up` lists the recovery module's data item as any item, and the restore points and asides are listed by `kept-points` (§517), @@ -85,7 +134,8 @@ are written into the lines of G-12 they name.* `mesh-broker`, which gains none; the seat `node-backup` gains nothing for kept data on the bus: its `aside` and `discard-aside`, the arguments `kept`, `form` and `fingerprint` of its `now` and `restore`, and its events `ended...` and `restored...` go from its definition in mesh-contracts, and - it gains only `item-started` and `item-ended` (§554); §450's `aside` answer is `restore-kept`'s with `aside-only`; + it gains only `item-started` and `item-ended` with their `` token, and names `holder:mesh-recovery@same-node` + among `restore`'s callers for the recovery module's own item (§554); §450's `aside` answer is `restore-kept`'s with `aside-only`; §406's catch-up is recorded in the point as before. Why: one owner of the acts (§554). How it is checked: unit (definitions), `node-backup` and `mesh-broker` hold none of the moved verbs; test 36. @@ -99,7 +149,11 @@ are written into the lines of G-12 they name.* and its `_first_views` names the stream `waiting`. So read: the node-engine seat's `running`, `composed`, `wait-held` and `wait-ended` (the last two kept on the stream `running` is kept on, last per subject, a node's module's newer one by `at` standing), by the assigner (§520), the condition keeper (§541), the builder (§546), the - node-engine and the grants part (§199); and `mesh-healers`' `attempt-changed` by the keeper (§541). This amends + node-engine and the grants part (§199), and the watchman for `delivery-waits` (§541); `mesh-healers'` + `attempt-changed` by the keeper (§541); `node-backup`'s `item-started` and `item-ended` and `mesh-wants`' `wanted` + by the recovery module, and the recovery module's `restore-started` and `restored` by the backup holder (§554). The + grants part grants each such reader, beside its durable consumer, the creation of an ordered consumer on that + stream with no durable name (the bus's consumer-create subject for that stream), from the reader's `consumes`. This amends §199's "each part's durable consumer starts from the last message per subject". Why: what a part heard must not depend on when it last restarted (review round 1, health and events 2, with running and testing 3). How it is checked: whole (assigner, keeper), a restarted assigner holds no want it did not hold before, and a restarted @@ -120,9 +174,17 @@ are written into the lines of G-12 they name.* verb's answer, naming it. The delivery plan derives the edge of kind 6 from the holders of that seat to the consumer, held until the holder runs a build whose catalog graph entry's seat definition (§194) gives the verb's answer every named field; §374's entry of kind 6 records the verb and its fields, and `outdated` (§375) says it - behind when the delivered version no longer gives them. The grants part's manifest names `mesh-broker`'s `list` - with `fields: [binding]`, and every part of G relying on a field G adds to a served answer names it so in G's - change. Why: the module graph orders a consumer after what it relies on, and a field is part of the contract + behind when the delivered version no longer gives them. Both forms stay: the short string where no field is relied on, the long form + where one is. On main mesh-sdk's manifest reader reads `invokes` as a list of strings, so the long form is read + first by §494's second reader release, merged and delivered before G's merge: its manifest reader takes either + 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 + 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 + 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. + a bus module whose definition gives `binding`, and not before; unit (the release's reader), both forms read. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index f96ae217..11578d85 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -5,6 +5,8 @@ design: 47 milestone: G updated: 2026-11-11 decisions: + - "ADR 0672" + - "ADR 0436" - "ADR 0670" - "ADR 0668" - "ADR 0667" -- 2.54.0 From 0b90251098295bd88a6675511c17bb3564bb8f61 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 13:06:37 +0100 Subject: [PATCH 6/9] Write the operator's answers of 13:06 on setting aside and on discard-aside's check Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../01-to-be/47-milestones/G-12-develop-answers-fourth.md | 6 +++--- .../47-milestones/G-14-recovery-module-and-review.md | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) 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 42c49686..746b2cc5 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 @@ -48,7 +48,7 @@ that round are written into the lines they name, here. Questions that an earlier (the module, the kept data, its form, the fingerprint, `snapshot`, `consumers` with each durable consumer's stream, name and acknowledged sequence, `size` in bytes, `at` and `catch-up`) comes on its event `ended` (§554); `restore-kept` takes `module`, `kept`, `form`, `fingerprint`, `why`, `from-backup` (yes or no) and one of `snapshot` (the restore point's - name), `aside-only` or `discard-aside` (an aside's `id`), and its result (`restored`, `aside`, `missing`, true when the + name), `aside-only` (the operator's hand tool alone, §554) or `discard-aside` (an aside's `id`), and its result (`restored`, `aside`, `missing`, true when the point is not in the recovery module's directory, and `error`) comes on its event `restored`. Both verbs answer only `accepted`, `busy` or `error` (§554). No argument `local-name` or `from` is taken, and a catch-up is never text. @@ -67,8 +67,8 @@ that round are written into the lines they name, here. Questions that an earlier 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 (whether that removal needs a person's warrant waits for the operator, ADR 0672 - review K3), + `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, + 13:06 CET, §554), restores the point under their names and sets each consumer to its recorded sequence. Each step is written first in the aside's record (`set aside`, `removed`, `restored`): when a step after the removal fails, `restore-kept` restores the aside under the live names and sets the consumers back before it answers `failed`, and at its start 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 f7fe2038..4d7966ef 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 @@ -74,8 +74,12 @@ are written into the lines of G-12 they name.* the want moving that module (`seat:node-engine`, proved by the bus as that node's account): the recovery module, which hears `mesh-wants`' `wanted` as §556 reads a stream, answers `error` naming the node unless that node's want names the module at `fingerprint` (for `snapshot-kept`) or moves it back from `fingerprint` (for `restore-kept`). - `kept-points` takes the default of a read (§391). `aside-only` and `discard-aside` wait for the operator (ADR 0672 - review, K3 and K5). Each act is written in the hand-act log of the seat `mesh-hand-acts` first, with the caller the + `kept-points` takes the default of a read (§391). The operator, 2026-11-11 13:06 CET at lead-47's terminal: setting + aside is a move, not a removal, so deleting the live objects once their aside is whole and checked (§516) needs + no person's warrant, the data staying in the aside until `discard-aside`; `aside-only` is the operator's hand tool + alone, `recovery_aside` (class `act`, callers `operator`); and `discard-aside` is checked by the identity of who + asked it: its caller is `operator`, as the bus proves the calling account, with no warrant asked per discard. The + want check above stays for the node-engine, which this line, not the identity, needs. Each act is written in the hand-act log of the seat `mesh-hand-acts` first, with the caller the bus proved and `why`; an act whose log write fails does nothing and answers `error`, naming the log (as §480). - **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 -- 2.54.0 From caa7a9265cf6b61356ac7cd981f2814b5892c920 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 13:08:11 +0100 Subject: [PATCH 7/9] Make identity the one check of the recovery module's callers, and give a bundle's finding writer its two runtime calls Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../G-14-recovery-module-and-review.md | 35 +++++++++++++------ 03-DESIGN/01-to-be/47-milestones/G.md | 2 +- 2 files changed, 25 insertions(+), 12 deletions(-) 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 4d7966ef..bf2d23ca 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 @@ -70,16 +70,15 @@ are written into the lines of G-12 they name.* `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. - - **Who calls it.** `snapshot-kept` and `restore-kept` are called by the node-engine holder of the node that holds - the want moving that module (`seat:node-engine`, proved by the bus as that node's account): the recovery module, - which hears `mesh-wants`' `wanted` as §556 reads a stream, answers `error` naming the node unless that node's want - names the module at `fingerprint` (for `snapshot-kept`) or moves it back from `fingerprint` (for `restore-kept`). + - **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); no want is read for it. `kept-points` takes the default of a read (§391). The operator, 2026-11-11 13:06 CET at lead-47's terminal: setting aside is a move, not a removal, so deleting the live objects once their aside is whole and checked (§516) needs no person's warrant, the data staying in the aside until `discard-aside`; `aside-only` is the operator's hand tool - alone, `recovery_aside` (class `act`, callers `operator`); and `discard-aside` is checked by the identity of who - asked it: its caller is `operator`, as the bus proves the calling account, with no warrant asked per discard. The - want check above stays for the node-engine, which this line, not the identity, needs. Each act is written in the hand-act log of the seat `mesh-hand-acts` first, with the caller the + alone, `recovery_aside` (class `act`, callers `operator`); and `discard-aside` and the hand tools are checked by the + identity of who asked: they are taken only from the operator's account as the bus proves it, and from sessions + acting on it as for every `act` tool (§391), with no warrant asked per act. Each act is written in the hand-act log of the seat `mesh-hand-acts` first, with the caller the bus proved and `why`; an act whose log write fails does nothing and answers `error`, naming the log (as §480). - **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 @@ -88,7 +87,7 @@ are written into the lines of G-12 they name.* - **Its account.** Composed by the grants part from the seat's definition and the manifest: 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` and of `mesh-wants`' `wanted`, each with the ordered + 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 `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 @@ -126,7 +125,7 @@ are written into the lines of G-12 they name.* design's (review rounds 1 and 2; ADR 0672). How it is checked: test 36 with the real backup holder and the recovery module; unit (grants part), the recovery module's account holds exactly the grants above, a call outside its names refused, both a point's old-section names and a template's binding granted, and the bus module's and the snapshot - account's hold no restore; whole (recovery module), a call from a node holding no such want answered `error`, a + account's hold no restore; whole (recovery module), a call from an account that is no node-engine's refused, a `busy` answer then the act on the other's `ended`, a second form change not woken by the first's `ended`, a dump asked during a restore starting after `restored`, a restart during the dump starting no restore, an act whose hand-act log write fails doing nothing, and an unrestored aside failing the health check. @@ -154,8 +153,7 @@ are written into the lines of G-12 they name.* `wait-held` and `wait-ended` (the last two kept on the stream `running` is kept on, last per subject, a node's module's newer one by `at` standing), by the assigner (§520), the condition keeper (§541), the builder (§546), the node-engine and the grants part (§199), and the watchman for `delivery-waits` (§541); `mesh-healers'` - `attempt-changed` by the keeper (§541); `node-backup`'s `item-started` and `item-ended` and `mesh-wants`' `wanted` - by the recovery module, and the recovery module's `restore-started` and `restored` by the backup holder (§554). The + `attempt-changed` by the keeper (§541); `node-backup`'s `item-started` and `item-ended` by the recovery module, and the recovery module's `restore-started` and `restored` by the backup holder (§554). The grants part grants each such reader, beside its durable consumer, the creation of an ordered consumer on that stream with no durable name (the bus's consumer-create subject for that stream), from the reader's `consumes`. This amends §199's "each part's durable consumer starts from the last message per subject". Why: what a part heard must not @@ -192,3 +190,18 @@ are written into the lines of G-12 they name.* (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. +- §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 + the subject the module's membership names for it (its old stream's `mesh.stream..event..` or + its template binding's, §529; ADR 0363 §8) and answers only once the bus acknowledged it, with its sequence, or + `{error}`; `mesh/subscribe` takes `deliver: new`, and the tool runner makes an ordered consumer on the stream that + carries the pattern, starting at the next message, and delivers each message to the bundle as a subscription + delivers today. The writer says each finding event with `ack: true` (§383's one minute of resending counting from + an unanswered or `{error}` publish) and makes its consumer of `findings-asked` with `deliver: new` before its start + statement (§485). The tool runner states the capability word `finding-calls` (§488, §545) once it answers both, + and the bundle's writer fails at once, naming the word and the node, where it is not stated. Why: one writer in both + 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. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index 11578d85..065bee02 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -94,4 +94,4 @@ The part is fourteen files, each read whole, its design lines numbered across th - [G-11-develop-answers-review](G-11-develop-answers-review.md): the operator's answers of 2026-11-11 noon, the review of §456 to §479, and develop's next questions (§480 to §515). - [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 §558). +- [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). -- 2.54.0 From e164a663626185eb555da3be6adbf3336cddc9f4 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 13:08:53 +0100 Subject: [PATCH 8/9] Write the operator's 13:10 answer: a restore asked is done, identity being the one check Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../01-to-be/47-milestones/G-14-recovery-module-and-review.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 bf2d23ca..8c05ee45 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 @@ -72,7 +72,8 @@ are written into the lines of G-12 they name.* 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); no want is read for it. + asked is the check (the operator, 13:06 CET), every act logged; no want is read for it, since the operator, + 2026-11-11 13:10 CET: "if something asks for a restore, you should do it; he'll have his reasons". `kept-points` takes the default of a read (§391). The operator, 2026-11-11 13:06 CET at lead-47's terminal: setting aside is a move, not a removal, so deleting the live objects once their aside is whole and checked (§516) needs no person's warrant, the data staying in the aside until `discard-aside`; `aside-only` is the operator's hand tool -- 2.54.0 From 040ad22e691d560fc22b5d3cb93e434348604272 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 13:12:42 +0100 Subject: [PATCH 9/9] Keep setting aside and discarding an aside on the operator's hand tools, refused by restore-kept Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../G-12-develop-answers-fourth.md | 14 ++++++++------ .../G-14-recovery-module-and-review.md | 17 +++++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) 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 746b2cc5..25a6d6e5 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 @@ -47,8 +47,9 @@ that round are written into the lines they name, here. Questions that an earlier kept data's local name), `form`, `fingerprint`, `why` and `catch-up` as `{tool, arguments}` (§451); its point (the module, the kept data, its form, the fingerprint, `snapshot`, `consumers` with each durable consumer's stream, name and acknowledged sequence, `size` in bytes, `at` and `catch-up`) comes on its event `ended` (§554); `restore-kept` takes - `module`, `kept`, `form`, `fingerprint`, `why`, `from-backup` (yes or no) and one of `snapshot` (the restore point's - name), `aside-only` (the operator's hand tool alone, §554) or `discard-aside` (an aside's `id`), and its result (`restored`, `aside`, `missing`, true when the + `module`, `kept`, `form`, `fingerprint`, `why`, `from-backup` (yes or no) and `snapshot` (the restore point's name), + and refuses `aside-only` and `discard-aside`, answering `error` naming them: setting aside without restoring and + discarding an aside are the operator's hand tools alone (§554); its result (`restored`, `aside`, `missing`, true when the point is not in the recovery module's directory, and `error`) comes on its event `restored`. Both verbs answer only `accepted`, `busy` or `error` (§554). No argument `local-name` or `from` is taken, and a catch-up is never text. @@ -74,14 +75,15 @@ that round are written into the lines they name, here. Questions that an earlier restores the aside under the live names and sets the consumers back before it answers `failed`, and at its start the recovery module does the same for every aside whose record ends at `removed`, so a failed or stopped restore never leaves the kept data gone. The aside's `id` is that directory's name, its `name` the bus name it was live - under, its `size` its bytes on disk; nothing of an aside stays on the bus. `discard-aside` removes that directory + under, its `size` its bytes on disk; nothing of an aside stays on the bus. `recovery_discard_aside` (§554) removes that directory and nothing else, an `id` holding `.`, `..` or `/` refused. A restore point is removed only as §551 says. This completes §405 and §406. Why: the operator's answer of 12:55 CET (§554) puts the taking and restoring of a module's kept data on the live bus on a module of its own; the server restores a stream only where no stream of that name exists, so the live data is set aside as a snapshot before the point takes its name (develop questions BKP1, BKP3 and NATS4's argument names; review round 1, running and testing 1, seams and tools' notes). How it is checked: - unit (mesh-recovery's contract), each verb's input and answer read, an input naming `from` refused; whole (recovery + unit (mesh-recovery's contract), each verb's input and answer read, an input naming `from` refused; a node-engine's + `restore-kept` with `aside-only` or `discard-aside` refused, naming them; whole (recovery module, backup holder, bus), a point is taken, its directory removed, brought back with `from-backup` and restored, the live data's aside listed with its size and the consumer at its recorded sequence; a point missing from both says `restore point missing`; a restore made to fail after the removal, and one stopped there and started again, leave @@ -95,7 +97,7 @@ that round are written into the lines they name, here. Questions that an earlier made. Why: a module's tools read everything it holds (*Tools are how a module is spoken to*), and the list is the holder's own fact (ADR 0596 §2; develop question BKP2; review round 1, seams and tools 4). How it is checked: whole (recovery module), `kept-points` lists a point and an aside with their sizes, an aside is gone after - `discard-aside`, and a restart lists the same. + `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.seat..event.` followed by the subject, each part filled in the definition's order: the seat @@ -537,7 +539,7 @@ that round are written into the lines they name, here. Questions that an earlier beside a `restore-kept` (§554). Each removal is written in the hand-act log of the seat `mesh-hand-acts` and said as `mesh-recovery`'s event `point-removed` on `mesh.seat.mesh-recovery.event.point-removed..` (`module`, `kept`, `form`, `fingerprint`, `snapshot`, `at`), kept 90 days (§396). The backup holder's copies go by its - repository's own rotation (ADR 0235 §4). Data set aside is removed only by `discard-aside` (§516). This completes + repository's own rotation (ADR 0235 §4). Data set aside is removed only by `recovery_discard_aside` (§554). This completes §516 and answers issue 1921's note on the retention of restore points. Why: the operator's answer; the event, the checks and their order are the design's. How it is checked: whole (recovery module), a point older than 90 days is removed and said, the newest point of a kept data older than 90 days stays, and a removal due during a restore runs 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 8c05ee45..927cab97 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 @@ -76,15 +76,15 @@ are written into the lines of G-12 they name.* 2026-11-11 13:10 CET: "if something asks for a restore, you should do it; he'll have his reasons". `kept-points` takes the default of a read (§391). The operator, 2026-11-11 13:06 CET at lead-47's terminal: setting aside is a move, not a removal, so deleting the live objects once their aside is whole and checked (§516) needs - no person's warrant, the data staying in the aside until `discard-aside`; `aside-only` is the operator's hand tool - alone, `recovery_aside` (class `act`, callers `operator`); and `discard-aside` and the hand tools are checked by the - identity of who asked: they are taken only from the operator's account as the bus proves it, and from sessions + no person's warrant, the data staying in the aside until the operator discards it; setting aside without restoring is + the operator's hand tool `recovery_aside`, and discarding an aside the hand tool `recovery_discard_aside`, both of + class `act`, callers `operator` and `sessions`, written in the hand-act log first; `restore-kept` refuses + `aside-only` and `discard-aside`, naming them (§516). The hand tools are checked by the identity of who asked: they are taken only from the operator's account as the bus proves it, and from sessions acting on it as for every `act` tool (§391), with no warrant asked per act. Each act is written in the hand-act log of the seat `mesh-hand-acts` first, with the caller the bus proved and `why`; an act whose log write fails does nothing and answers `error`, naming the log (as §480). - **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 skip the want check, since the - operator's call is the reason. + `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 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 @@ -129,17 +129,18 @@ are written into the lines of G-12 they name.* account's hold no restore; whole (recovery module), a call from an account that is no node-engine's refused, a `busy` answer then the act on the other's `ended`, a second form change not woken by the first's `ended`, a dump asked during a restore starting after `restored`, a restart during the dump starting no restore, an act whose - hand-act log write fails doing nothing, and an unrestored aside failing the health check. + hand-act log write fails doing nothing, an unrestored aside failing the health check, a node-engine's `restore-kept` with `aside-only` or `discard-aside` + refused, and the operator's `recovery_discard_aside` removing the aside and writing the log. - §555 **What moves to the recovery module.** §296 asks `mesh-recovery`'s `snapshot-kept` and waits for its `ended`, never the seat `node-backup`; §297 asks its `restore-kept` and waits for its `restored`; §302's `backed-up` lists the recovery module's data item as any item, and the restore points and asides are listed by `kept-points` (§517), - `discard-aside` being `restore-kept`'s; §303 and test 36 name the recovery module where they named the backup + an aside discarded by the hand tool `recovery_discard_aside` (§554); §303 and test 36 name the recovery module where they named the backup holder and the bus module; §337's and §405's callers `holder:node-backup@same-node` go with the two verbs of `mesh-broker`, which gains none; the seat `node-backup` gains nothing for kept data on the bus: its `aside` and `discard-aside`, the arguments `kept`, `form` and `fingerprint` of its `now` and `restore`, and its events `ended...` and `restored...` go from its definition in mesh-contracts, and it gains only `item-started` and `item-ended` with their `` token, and names `holder:mesh-recovery@same-node` - among `restore`'s callers for the recovery module's own item (§554); §450's `aside` answer is `restore-kept`'s with `aside-only`; + among `restore`'s callers for the recovery module's own item (§554); §450's `aside` answer is `recovery_aside`'s; §406's catch-up is recorded in the point as before. Why: one owner of the acts (§554). How it is checked: unit (definitions), `node-backup` and `mesh-broker` hold none of the moved verbs; test 36. -- 2.54.0