diff --git a/00-META/glossary.md b/00-META/glossary.md index 80130957..09d27f4e 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -978,7 +978,7 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564, 0668. takes a restore point of a module's kept data on the live bus before the module moves across a `form` change, sets the live data aside and restores the point at a revert, and lists and removes restore points; the bus module and the snapshot account restore nothing into the live bus, and the backup holder only stores copies (design 47 G §554). - **aside** — live kept data a restore set aside, removed only by the operator's hand tool `recovery_discard_aside`. + **aside** — live kept data a restore set aside, removed only by the hand tool `recovery_discard_aside`, which the operator and sessions call. *Not:* ~~restorer~~, ~~restore account~~ - **disk readings** — the size of each data item a node's modules declare, measured by its `measure` and kept with its largest size in seven days by disk-load on that node, in its own state, one key diff --git a/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md b/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md index c8fba9cf..2ad4c5b9 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md +++ b/03-DESIGN/01-to-be/47-milestones/G-14-recovery-module-and-review.md @@ -194,8 +194,7 @@ are written into the lines of G-12 they name.* every reader of `mesh-catalog`'s `revision` through mesh-sdk's client (the bus module, the status view, the condition keeper, the tool runner, the assigner, the grants part and the runner), with `none-yet` (§530); the status view, `mesh-catalog`'s `entry` with `manifest-digest` - (§506). The grants part names none: design 25 *bindings-first-view* §9 (on the branch - `design/25-bindings-from-the-list`) composes a binding from the fields `list` answered before G, so it relies on no + (§506). The grants part names none: design 25 *bindings-first-view* §9 composes a binding from the fields `list` answered before G, so it relies on no new field of `list`, and naming `binding` would close the circle of §569. The recovery module reads the manifest whole the entry carries (§443), a field of before G. Any other part of G relying on a field G adds to a served answer names it so in G's change, which the code review checks. Why: the module graph orders a consumer after what it relies on, and a field is part of the contract (lead-47's question from the operator's "the module graph is for this", 2026-11-11). How it is checked: unit diff --git a/03-DESIGN/01-to-be/47-milestones/G-15-develop-answers-fifth.md b/03-DESIGN/01-to-be/47-milestones/G-15-develop-answers-fifth.md index 93602658..88b9ad61 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-15-develop-answers-fifth.md +++ b/03-DESIGN/01-to-be/47-milestones/G-15-develop-answers-fifth.md @@ -30,13 +30,12 @@ decisions: One file of milestone G's part of [design 47](../47-delivery-from-commit-to-delivered.md); the part opens at [G.md](G.md). Its design lines are numbered across every file of the part and cited as "design 47 G §n". -*§560 to §586 answer develop's fifth batch of questions (lead-47's input of 2026-11-11, after round 3 of the review -of [G-12](G-12-develop-answers-fourth.md) and [G-14](G-14-recovery-module-and-review.md)), the reviewers' round-3 -notes on §516 to §559, and the knowledge base's review of ADR 0672 (the recovery module). Six carry the operator's -answers of 2026-11-11 at lead-47's terminal, each line saying so: §562 (the recovery module's move), §563 (a put-off -dump), §568 (the builder's retry on an event), §570 (refused asks listed) and §575 (a failed build's words). The -rest are of internal form only: they change nothing the operator sees, nobody's permissions and no data kept or -removed. A note that corrects a line of G-12 or G-14 is written in that line; here only what is new.* +*§560 to §586 answer develop's fifth batch (lead-47's input of 2026-11-11), the reviewers' round-3 notes on §516 to +§559, the knowledge base's review of ADR 0672 (the recovery module), and round 1 of this batch's own review, written +into the lines it names. Seven carry the operator's answers of 2026-11-11 at lead-47's terminal, each line saying +so: §562 (the module's move), §563 (a put-off dump), §568 (the builder's retry), §570 (refused asks), §575 (a failed +build's words), §584 (the forge and image registry in the foundation) and §554's account. The rest are of internal +form only. A note that corrects a line of G-12 or G-14 is written in that line; here only what is new.* ## The recovery module @@ -71,29 +70,49 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - **The 90-minute bound** of the node agent's wait (§554) counts from its first ask for that point or restore, every `busy` and every ask again within it. Past it, the node agent's have says the wait and the watchman's `delivery-waits` raises it. + - **How the node agent hears and keeps it.** The node-engine's manifest `consumes` `mesh-recovery`'s `ended` and + `restored`, read as §556 has it. Before it asks, the node agent writes in its have the want's wait with `ask-at` + (its first ask's time); once answered `accepted`, it writes the `act` there too. After a restart it reads that + have: an `act` whose ending event the read of §556 already gives is taken as heard, and one not yet ended is + waited on, asked nothing again; a wait with no `act` yet is asked again within its bound. + - **What the node agent does with each ending.** + + | outcome, `error` | what the node agent does | + |---|---| + | `done` | goes on with the move or the revert | + | `failed`, `stopped` (a restart or a move cut the act off, §562) | asks again, within the same bound | + | `failed`, `cancelled` (§566) | asks nothing again; holds the move, the reason `cancelled` with the hand act's `why` | + | `failed`, `cap` (§564) | holds the move, the reason the error's own words | + | `failed`, `restore point missing` or `restore point damaged` (§516, §567) | holds the revert as §300 has it, the reason the error's own words | + | `failed`, any other error | holds the move, the reason the error's own words | + + A hold is the phase `held` with that reason, said as `wait-held` (§541), and ends on a new want or the + operator's hand act (`recovery_retry`, then a new ask of the node agent on that act's ending). This replaces §554's list of events. Why: an event a caller cannot tie to its own call wakes it on another's failure, and an act whose end is never said holds its caller unseen (round 3, health and events, items 1 and 2; running and testing, item 3). How it is checked: unit (mesh-recovery's contract), each event's fields read; whole (recovery module), a retried act's caller not woken by the first act's `failed`, an expiry run that removes nothing says `expiry-ended` with `removed` 0, an act cut off by a restart ends `failed` with `stopped`; unit (node agent), - `busy`, then the other act's ending event, then an ask again within one bound, and the bound passed after repeated - `busy` answers. + `busy`, then the other act's ending event, then an ask again within one bound, the bound passed after repeated + `busy` answers, and each row of the table; whole (node-engine, recovery module), a node-engine restarted during a + restore takes that restore's `restored` and asks nothing again. - §561 **The recovery module's and the backup holder's findings, each with its scope, id and clearing.** (§554) Each is written by mesh-sdk's finding writer (§461) as the key `..` (ADR 0564 §2), each token escaped as a key's name is (§73), and said on the part's finding stream (§383, §529): - `points-filling`: the recovery module's, scope `mesh-recovery`, id the node it runs on. It is written when the - directory of its data item `points` passes the fill mark of its cap (§564), measured after each act and at each - of its disk readings, naming the size, the cap and the five largest points and asides. It is cleared when a + directory of its data item `points` passes the fill mark of its cap (§564), which the recovery module measures + itself at its start, after each act and after each expiry run, naming the size, the cap and the five largest points and asides. It is cleared when a measure finds it below the mark. - `recovery-waits`: the recovery module's, scope the module whose act waits, id the act's `act`. It is written 60 minutes after the act began waiting on the dump of the bus's item (§563), naming the item and since when. It is cleared when the act starts its work, or ends. - `backup-waits`: the backup holder's, scope its node, id the item that waits (`.`, one escaped - token). It is written 60 minutes after the item was put off (§563), naming the restore's `act`. It is cleared + token). It is written 60 minutes (its setting `put-off-bound`, §557) after the item was put off (§563), naming the restore's `act`. It is cleared when the item starts again. - - `aside-unrestored`: the recovery module's, scope the module, id the aside's `id`. It is written when its health - check finds an aside whose record ends at `removed` and that no running act holds (§554). It is cleared when that + - `aside-unrestored`: the recovery module's, scope the module, id the aside's `id`. It is written at an act's end + and at the module's start for each aside whose record ends at `removed` and that no running act holds, which + the health check also fails on (§554). It is cleared when that aside's record ends at `restored` or the aside is discarded. The recovery module and the backup holder of G are observers on the condition keeper's list of sources (§387, @@ -112,10 +131,26 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he with an empty directory: `kept-points` lists nothing of the old node's, and a `restore-kept` of a point left there answers `missing`. Its `from-backup: yes` then asks the new node's backup holder, which holds no copy of it, so `restored` says `failed` with `restore point missing` and §300 holds the revert. The placement verb asks the - recovery module nothing, and §554 no longer refuses a move while a point younger than 90 days is listed. Why: the - operator's answer (the knowledge base's review of ADR 0672, R6). How it is checked: unit (assigner), a placement - that moves the holder of `mesh-recovery` while points are listed is made, asked as ADR 0539 §14 has it; whole - (recovery module), on an empty directory a restore of an unknown point says `restore point missing`. + recovery module nothing, and §554 no longer refuses a move while a point younger than 90 days is listed. + - **An act a move cuts off.** When its node's want is written as removed, the old instance takes no new call + (answering `error` naming the move) and, before it stops, ends its running act through its failure path (§516): + a restore past its removal puts the aside back under the live names and sets the consumers back; then it ends + every accepted act, the running one included, with its ending event, `outcome` `failed` and `error` `stopped`. + - **A stop it could not finish** (the old node gone or killed): the new instance, at its start, reads the seat's + stream as §556 has it, and ends `failed` with `stopped` every act whose `restore-started` or acceptance (its + `ended` or `restored` missing) the stream holds with no ending event, and writes `aside-unrestored` for each such + restore, naming the old node, since its aside is on the old node's disk. + - **Who asks again.** The node agent and the backup holder hear those endings (§560, §563); the node agent asks + again the new holder, which it finds from the catalog half's claims of `mesh-recovery`, and the backup holder + starts a put-off item again on a `stopped` `restored`. + + Why: the operator's answer (the knowledge base's review of ADR 0672, R6); the ending of a cut-off act is the + design's (round 1 of this batch's review, running and testing R2, health and events H1). How it is checked: unit + (assigner), a placement that moves the holder of `mesh-recovery` while points are listed is made, asked as ADR + 0539 §14 has it; whole (recovery module), on an empty directory a restore of an unknown point says `restore point + missing`; a move during a restore after its removal leaves the live data as before and ends the act `stopped`; a + new instance whose old one was killed during a restore ends that act `stopped` and writes `aside-unrestored`; a + put-off dump starts again after it. - §563 **When a dump and a restore start at one moment, the dump gives way.** (§554) Each side says its start before it reads the other's: - **The backup holder** of G, for the item of the holder of `mesh-broker` (read from the seat list's definition @@ -125,6 +160,8 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he it hears `restored` with that `act`. - **The recovery module** says `restore-started`, then reads the item events of the bus's item. While an `item-started` has no `item-ended`, it waits, and takes no aside. + - **Said means acknowledged.** Each side reads the other's only once the bus acknowledged its own start event on + its stream. Since each side says its start before it reads, at least one side sees the other, and the backup holder always gives way, so the two never overlap. A dump that has run since before the restore began is waited for. The backup @@ -139,8 +176,9 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he Why: §554 left open which side gives way when both start at once (round 3, running and testing, item 1); how a put-off item counts is the operator's answer (the knowledge base's review of ADR 0672, R5). How it is checked: - whole (backup holder, recovery module, the real bus), a dump and a restore asked at one moment never overlap and - the item runs after `restored`; a put-off item raises no `backup-stale` and is not counted failed; a backup holder + unit (backup holder and recovery module against a fake bus), both orders of "say, then read" in both sides, each + ending with no overlap; whole (backup holder, recovery module, the real bus), a dump and a restore asked at one + moment never overlap and the item runs after `restored`; a put-off item raises no `backup-stale` and is not counted failed; a backup holder restarted during an item ends it `failed` with `stopped`, and a waiting restore then starts. - §564 **The cap is a setting, an act that would pass it is refused before it removes anything, and a failed act leaves no half-made point or useless aside.** (§554, §557) @@ -152,6 +190,8 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he ends `failed` with `error` `cap`, naming the cap, that size and the directory's fill, before any aside is taken and so before any live object is removed. A `snapshot-kept` that ends so holds the move as a failed backup does (§296). A point or an aside that passes the cap while it is taken (the stream grew) ends its act the same way. + - **Brought back.** A point brought back from the backup (`from-backup: yes`, §516) is not counted against the + cap: it lies beside its path only while its restore runs, and is removed when the restore ends. - **On a failure.** An act that ends `failed` removes its point under a temporary name (§516) then, not only at the next start. An aside whose record ends at `set aside` (the live objects were never removed) is removed when its restore fails or stops before the removal, and at the start, since the live data was never touched. @@ -164,21 +204,25 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - §565 **The grants part reads the restore points' names through `kept-points`, and composes without them until it answers.** (§554) The bus names of the recovery module's account include those a restore point names (§554). The grants part reads them by its `invokes` of `seat:mesh-recovery.kept-points`, a kept call (ADR 0668 §1). It asks - again on every `ended` with `outcome` `done` of `mesh-recovery`, which it consumes (ADR 0668 §4), so a point's - names are granted before any restore of it. Until `kept-points` first answers, the grants part composes the - recovery module's account from the catalog half's kept entries alone (§443) and says so in `grants_first_views` - (§556). The edges between the two (kind 5, the grants part has acted for the recovery module's account; kind 6, + again on every `ended` with `outcome` `done`, every `point-removed` and every `expiry-ended` of `mesh-recovery`, + which it consumes (ADR 0668 §4), so a point's names are granted before any restore of it. Until `kept-points` + first answers after a start, the grants part keeps the names it last granted from it, kept in its state + `memberships` with the account, and adds the catalog half's kept entries (§443); it takes no name away before + that answer, and says the wait in `grants_first_views` (§556). The edges between the two (kind 5, the grants part has acted for the recovery module's account; kind 6, the recovery module serves `kept-points`) are a cycle of delivery edges, delivered as one step (§49). Why: the account must follow the points without the grants part reading another part's state (ADR 0596; round 3, seams and - tools' notes). How it is checked: unit (grants part), an account composed from the catalog half alone while - `kept-points` is unanswered, and a point's old-section names added after an `ended`. + tools' notes; running and testing R3). How it is checked: unit (grants part), a restarted grants part with + `kept-points` unanswered keeps a point's names it granted before, and a point's old-section names are added after + an `ended` and taken away after its `point-removed`. - §566 **Two more hand tools: put an aside back, and cancel a waiting act.** (§554, §516) Each is of class `act`, with callers `operator` and `sessions`, checked by the identity of who asked (§554), written first in the hand-act log of the seat `mesh-hand-acts` with the caller the bus proved and `why`, and an act of its own that runs one at a time (ADR 0672): - `recovery_restore_aside` (`module`, `kept`, `id`, `why`) restores an aside under the live names it was taken from, for an aside whose record ends at `removed` that no running act holds (the case `aside-unrestored` names, §561). - It refuses, naming them, while live objects of those names exist, and it ends with `restored`. + It refuses, naming them, while live objects of those names exist (an aside taken by `recovery_aside` is put + back by this tool too, the refusal naming `recovery_aside`'s aside when the names are live), and it ends with + `restored`. - `recovery_cancel` (`act`, `why`) ends an accepted act that has not begun its work (one waiting on the dump, §563) with its ending event, `outcome` `failed` and `error` `cancelled`. It refuses an act that has begun, naming its step, since a restore cut off after its removal is put back only by its own failure path (§516). @@ -188,13 +232,17 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he `recovery_restore_aside` and refused while the live names exist; a waiting act cancelled and a running one refused. - §567 **A restore point is checked by the digest of its bytes before it is restored.** (§516) A point's record carries `digest`, the `sha256:` digest of each bus object's snapshot file, written when the point is renamed whole - (§516). `kept-points` lists it, and `ended` carries it with the point. Before a restore takes any aside, it computes - the digest of the bytes it is about to restore, whether from its own directory or brought back by the backup - holder (`from-backup: yes`). It compares that digest with the one `ended` carried when the seat's stream still - holds that point's `ended`, and with the record's otherwise. A mismatch ends the act `failed` with `restore point + (§516). The same digests are kept on the bus, apart from the bytes: in the recovery module's own state `digests` + (its kept data, by the template `state`), one key per point, its `snapshot` escaped as a key's name is (§73), + written before `ended` is said and deleted when the point is removed (§551); about 0.3 KiB a key, capped at 4 MiB. + `kept-points` lists the digests, and `ended` carries them with the point. Before a restore takes any aside, it + computes the digest of the bytes it is about to restore, whether from its own directory or brought back by the + backup holder (`from-backup: yes`), and compares it with the key in `digests`; the record in the directory is never + the reference. The state holds digests only and lists nothing, so §517's "no `bus-points` state" stands for the + list. A mismatch ends the act `failed` with `restore point damaged`, naming the point and the bus object, and §300 holds the revert. Why: a point brought back from the backup was restored unchecked (round 3, security and upgrade's notes). How it is checked: whole (recovery module, backup - holder), a point whose file was altered after it was taken is refused before any aside is taken. + holder), a point whose file and record were both altered after it was taken is refused before any aside is taken. ## The builder @@ -221,9 +269,21 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he node is heard again. The builder hears both through its consumers of the node-engine seat, read as §556 has it. It builds a key again at most once per event heard, and only while the key's newest revision is that failure, so one event starts one revision. A revision that fails again on a transport fault is said as every failed key is, - its error opening `transport fault:` again, and waits for the next such event. A key failed for any other cause + its error opening `transport fault:` again, and waits for the next such event. The builder's manifest `consumes` + both events. + - **Three automatic builds, then the operator is asked.** The operator, 2026-11-11 about 14:55 CET at lead-47's + terminal: the builder counts, in the key's newest revision, the revisions events started since the last one a + person started (`event-builds`). After three that failed, the next such event starts nothing: the builder asks + the operator once, through the seat `operator-channel`'s ask (level `approve`, options *Build once more* and + *Leave it*, `about` the key so a newer ask replaces it, ADR 0666 §12), naming the module, the fingerprint and + the last error whole. *Build once more* builds one revision, counted as a person's, so the count starts again; + *Leave it*, a decline or the ask's expiry leaves the key failed until `builder_build_again` or a new + fingerprint. The failure stays said throughout. A key failed for any other cause is never built again on these events. `builder_build_again` (ADR 0668 §5) and the builder's next start build it - again too, as before. The builder's read tool of a key names the event that built its newest revision again. + again too, as before. The builder's read tool of a key names the event that built its newest revision again, + `event-builds`, and the open ask. + - **Before develop.** These lines rest on the record of the builder's retry, which changes ADR 0664 §3 (in force); + it is accepted before G's builder is built from them, and its number replaces "number to come" here. Why: a read the builder made met `no responders` and failed a key, which then stayed failed for about 45 minutes (issue 1612, 2026-11-11), against ADR 0664 §3 (a key is not written failed while its step runs on); the retry on an @@ -231,7 +291,9 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he `building` and the next revision ends it; a third-run transport fault's key error opens `transport fault: no responders`, and a compile error's does not; a `running` of a holder of that build seat builds the key again once, and a second `running` while the new revision builds starts nothing; a node's `manifest-reads-changed` builds again - a key failed on that node; a key failed on a compile error is not built again on either event. + a key failed on that node; a key failed on a compile error is not built again on either event; after three failed + event-started revisions the fourth event asks the operator once and builds nothing, and *Build once more* builds + one. ## The order of a change @@ -243,23 +305,28 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he start before a delivery that waits for that build (ADR 0666 §14). Two kinds of edge enter that graph: - **A seat verb a build calls.** When a build module's step calls a seat verb while it builds (the image build module calling `container-image-registry`'s `mirror` for a base image), its manifest names it in `invokes`, and - the indexer derives an edge of kind 3 from that seat's holders to every artifact the build module builds that - the call serves (each image artifact that names a base image). + the indexer derives an edge of kind 3 from that seat's holders to every image artifact the build module builds. + The edge is met by the holder that runs when the build starts, whatever its version: it orders a build after a + running holder, never after the holder's new version. So the holder's own artifacts are built through its + running version, and the edge makes no cycle through them. - **A grant from a first view.** A binding the grants part writes for a holder from its first view of a served - answer (design 25 *bindings-first-view* §4 and §9, being written on the branch `design/25-bindings-from-the-list`) + answer (design 25 *bindings-first-view* §4 and §9) is an edge of kind 5 from the grants part to that holder. Where the grants part names a field of that answer - (§558), an edge of kind 6 runs from the answer's seat holder to the grants part. + (§558), an edge of kind 6 runs from the answer's seat holder to the grants part. Its new version must run, so this + edge is met only by that version. The circle of 2026-11-11 was such a cycle: the bus module's image waits, by kind 3, for the registry holder's delivery; the registry holder waits, by kind 5, for its binding `mirrors` from the grants part; and the grants part - waited, by kind 6 on `mesh-broker`'s `list` with `binding`, for the bus module's new version. It is refused at its + waited, by kind 6 on `mesh-broker`'s `list` with `binding`, for the bus module's new version: the kind-3 edge waited + for a registry that served `mirror`, which needed its binding, which needed the new bus module. It is refused at its check, naming the three artifacts. Design 25 *bindings-first-view* §9 removes the kind-6 edge: the grants part composes a binding from the fields `list` already answers, so it relies on no new field and names none (§558). G's delivery then puts the grants part before the bus module's build. Why: the circle mixed a build edge and a delivery edge, and no rule saw it (issue 1966, notes 203 and 204; the operator, 2026-11-11 13:30 CET, on design 25's §9). How it is checked: unit (indexer's graph), a cycle of kinds 3, 5 and 6 is refused naming its three artifacts and their kinds; a cycle of kinds 5 and 6 alone is one step; a build module's `invokes` of `mirror` gives - a kind-3 edge to its image artifacts. + a kind-3 edge to its image artifacts; the image registry's own image, built through its running holder, is not + refused. ## Asks to the operator @@ -274,20 +341,26 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - **Every asker ends its request on it.** mesh-sdk's asker client gives its caller `refused` with `why` as soon as it hears it. An asker's tool or act that waits on that ask answers `refused` with why, never "not answered yet"; the bus module's `nats_recreate` and `nats_drop` are among them. - - **The lifetime is held where every ask is made.** mesh-sdk's asker client, through which every asker publishes + - **The lifetime is held where every ask is made.** mesh-sdk's asker client changes in G's mesh-sdk release, which + every asker is built against (§47). mesh-sdk's asker client, through which every asker publishes an ask, refuses before it publishes an ask whose expiry passes the bound of its highest option level (`approve` 24 hours, `destroy` 10 minutes, ADR 0259 §4), naming the level and the bound. The bus module's keeper asks for its level's bound in place of its fixed 12 hours (`AskLasts`). - **The operator sees a refused ask.** The operator, 2026-11-11 about 14:15 CET at lead-47's terminal: the router keeps each ask it refuses at take-in in its bucket `asks` under `.` as a closed record, with `outcome` - `refused`, `why` in the router's words and `at`, for 30 days like every closed record (ADR 0259 §3). The seat's + `refused`, `why` in the router's words and `at`, for 30 days like every closed record (ADR 0259 §3). It writes + the record only by a create: when `.` holds a record already (an open ask, its tickets with it), the + refusal writes nothing there and is said on `decided` alone. The operator, 2026-11-11 about 15:15 CET ("just make + it configurable"): the router keeps at most `refused-kept` refused records per asker, a setting of the router, + 20 unless set, removing the oldest first, each also gone after its 30 days, each removal written in its journal. The seat's `asks` lists it with that outcome and words, as an ask that ended. Why: ADR 0259 §4 refuses such an ask on the decided subject and bounds a destroy option at 10 minutes, and neither reached the asker; the list is the operator's answer. How it is checked: unit (mesh-sdk's asker client), a table over the levels in which an expiry at the bound is published and one past it is refused before it is published, and a `refused` that ends the request with why; the code review refuses an ask built outside the client; whole - (router, bus module), a refused ask is said on `decided`, `asks` lists it `refused` with the router's words, and + (router, bus module), a refused ask is said on `decided`, a refusal of an id that is open leaves the open record and its tickets as they + were, a twenty-first refusal of one asker removes its oldest, `asks` lists it `refused` with the router's words, and `nats_recreate` answers `refused` with those words. ## A module's packages @@ -307,6 +380,8 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he its member's head. Outside a group, the edge of kind 1 (§47) builds the dependent after the package's first publication, which G's delivery orders. - **The check's lock rule** never refuses a mesh package's entry, nor its absence. + - **The reuse key** of a build takes the versions it wrote for mesh packages, as the fingerprint takes them (ADR + 0573 §2), so a build is never reused across two of them. Why: ADR 0573 §2 has the build write a mesh package's version into the workspace, and a lock made by hand holds the integrity of a locally packed tarball, which the published one never matches (develop question of the forge @@ -321,7 +396,7 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he package's name to its module and artifact is the builder process's own, from its view of the catalog half (§348). The seat `mesh-builds`' `reserved`, given `name`, is answered by the builder's process from that view. The builder's tools bundle holds no catalog view: `builder_versions`, asked by a name, calls `reserved` (or, for a - module, the builder's own read verb) through its `invokes` and answers what the process answers. Why: one mapping + module, `reserved` with `module`) through its `invokes` and answers what the process answers. Why: one mapping and one view, in the part that keeps them (ADR 0596 §2; develop question BL7's name part). How it is checked: unit (builder), `reserved` with an npm name and with a Go module path answered from the view; whole (builder), `builder_versions` by name answers the same as `reserved`. @@ -336,21 +411,26 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - §574 **`toolchain-agreed` carries `digests` as a list.** (§472, §495) The event's field `digests` is a list, each item `{digest, nodes}`: the `sha256:` digest of the toolchain program and the nodes that reported it at the agreed version, sorted by digest, with each item's `nodes` sorted. It replaces the field `digest`, which the event no - longer carries. Why: §472 names one digest per group of nodes, and a list keeps two digests of one version apart + longer carries; a kept event of before G that carries `digest` alone is read as + one item with no `nodes`. Why: §472 names one digest per group of nodes, and a list keeps two digests of one version apart (develop question of the contracts developer). How it is checked: unit (mesh-builds' contract), the list read, and - an event carrying `digest` alone refused. + an event of before G carrying `digest` alone read as one item with no `nodes`, so a reader of G refuses none + and nothing waits for the next agreement. ## What a node-engine says of a build -- §575 **A want whose build is not done says how the build stands.** (§524) The operator, 2026-11-11 about 14:05 CET - at lead-47's terminal: where the node-engine's have says that a want's build is not done, it says how the build - stands, read from the build key it already reads for the want's artifacts. A failed build reads `the build of - at failed: `, the error whole, `transport fault:` included (§568). A - build that runs reads `the build of at is being built`. The have's phase is `held`, and its - `wait-held` carries the same words, which the watchman's `delivery-waits` names. "Is not done" is said no more. Why: - the operator's answer; a failed build said as not done left the operator and the watchers waiting for a build that - had ended. How it is checked: unit (node-engine), a want whose build key is failed says `failed:` with the key's - error, and one whose build runs says `is being built`. +- §575 **A want held for its build says how the build stands.** (§524) The operator, 2026-11-11 about 14:05 CET at + lead-47's terminal: the words "the build of at is not done" are the assigner's, the reason + of its hold under its first rule (no want for a fingerprint whose build is not done, ADR 0429 §6). That reason now + says how the build stands, from the builder's `build.ended` the assigner already consumes and from the build it + waits on: a failed build reads `the build of at failed: `, the error whole as + `build.ended` carries it, `transport fault:` included (§568); a build that runs reads `the build of at + is being built`. The assigner says it in its wait for that module, which the watchman's + `delivery-waits` names; the node-engine reads no build state for it (ADR 0596). "Is not done" is said no more. + Why: the operator's answer; a failed build said as not done left the operator and the watchers waiting for a + build that had ended (round 1 of this batch's review, seams and tools T1, placed the words in the assigner). How + it is checked: unit (assigner), a module whose build ended failed is held saying `failed:` with the error, and one + whose build runs saying `is being built`. ## The tool runner, and kept data at run time @@ -406,28 +486,46 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he `pinned` heard twice pins once. At about 20 revisions pinned a day for 90 days, about 1,800 keys of about 0.2 KiB each, it is capped at 4 MiB (an estimate). - **Applying `pinned` and `freed`.** The indexer adds or removes a record's id by compare-and-set, and only then - acknowledges the event on its durable consumer of `mesh-delivery`'s events (§153). A `freed` said while it was - stopped is applied when it comes back, and one applied twice changes nothing. + acknowledges the event on its durable consumer of `mesh-delivery`'s events (§153). That consumer, when first + made, starts at the stream's first message. A `freed` said while it was stopped is applied when it comes back, + and one applied twice changes nothing. A `pinned` of a revision the indexer no longer holds writes its key with + the record's id and `missing: true`, and the finding `pin-missing` (scope the indexer, id the revision) naming + the record, which `indexer_unpin` clears. + - **The gate states its pins.** At each start the gate says `pinned` again for every revision each record it keeps + pins, then once `pins-stated` (`at`), so pins said before the indexer's consumer existed, or older than the + stream keeps, are heard again; the set form absorbs the repeats. - **Current.** A revision is current while it is the newest revision the indexer took in on its watched branch, whichever watched branch that is, or the newest revision of an open candidate (its `candidates.`). It stops being current when a newer revision of its branch is taken in, or when the candidate's `heads-replaced` is heard (its heads replaced, or its pull request closed) or the candidate is taken in. - **Removal.** When a revision's list is empty and it is not current, the indexer removes its revision object and - its key, and its entry objects as §179 has it. It removes one only on applying `freed` or on the revision ceasing - to be current, never on a timer. + its key, and its entry objects as §179 has it, but only while its consumer of `mesh-delivery`'s events is caught + up (none of its messages pending), so a `pinned` said before is applied first, and only once it has applied a + `pins-stated` said after its state `pins` was made. It judges on applying `freed`, on a revision ceasing to be + current, on being caught up, and at its start after both of these hold, when it judges every kept revision, so a + stop between a take-in and a removal keeps nothing for ever. Never on a timer. - **Inputs.** A catalog graph revision names no input revision. "The input revisions it names" (§178) are an assignment graph revision's catalog revisions, which a delivery record pins itself (§153), so the indexer keeps - each by its own list alone. The assigner keeps its pins of assignment graph revisions in the same form, in its own - kept data in the form §529 gives its part. + each by its own list alone. + - **The assigner** keeps its pins of assignment graph revisions in the same form and by the same rules: its old + `state` section `pins` (it is a foundation part, ADR 0619 §1), one key per assignment graph revision, capped at + 4 MiB; a revision is current while it is the newest the assigner wrote, or the newest it wrote for an open + candidate; its tools are `assigner_pins` and `assigner_unpin`, as the indexer's below. - **Its tools.** `indexer_pins` reads each kept revision with the records that pin it, whether it is current and why, and the store's fill against its cap. `indexer_consumer` reads each of the indexer's durable consumers: its stream, name, acknowledged sequence, the messages pending, and the last event applied with its `at`. Both are - of class `read`. + of class `read`. `indexer_unpin` (`revision`, `record`, `why`; class `act`, callers `operator` and `sessions`, + written in the hand-act log first) removes one record's id from a revision's list, for a `freed` that was lost, + and the removal rule then applies. Why: §153 and §178 said what is kept, not where or how it is counted (develop questions IX17 and IX18). How it is checked: unit (indexer), a revision pinned twice by one record and freed once is removed; a `freed` applied twice changes nothing; a revision of a second watched branch kept while current; a candidate's revision removed on - `heads-replaced`; whole (indexer, gate), a `freed` said while the indexer was stopped is applied after its start. + `heads-replaced`; a revision that stops being current while a `pinned` of it is pending is kept; a `pinned` of a + removed revision writes `pin-missing`; `indexer_unpin` frees a revision; whole (indexer, gate), a `freed` said + while the indexer was stopped is applied after its start; a `pinned` said before the indexer of G first started is + heard through the gate's restatement; an indexer stopped between a take-in and a removal removes the revision at + its start. ## Observers with more than one bundle, and the forge module's forms @@ -446,7 +544,8 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he - §581 **A `seen` key's 30 days are judged on each step end, with no timer.** (§487) The forge module writes `at` in each `seen` key's value, the time of the `step-ended` that wrote it. On each `step-ended` it handles, and at its start, it deletes the `seen` keys whose `at` is more than 30 days old, at most 100 for each event, the oldest first, - so the bus keeps no key time-to-live and no template option is added. The forge module being a foundation part + so the bus keeps no key time-to-live and no template option is added; its tool `git_seen_expire` (class `act`, + callers `operator` and `sessions`, written in the hand-act log) runs the same removal now. The forge module being a foundation part (§584), its state `failed-steps` is in its old `state` section, and a bundle reaches it through the tool runner's state call by its name, as every old section is reached. Why: §487's 30 days needed a trigger, and an event the module already handles is one (develop question of the forge module's developer). How @@ -477,13 +576,17 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he finding stream `-findings` as §529 has a foundation part declare them, and every other state in its old `state` section, the forge module's `failed-steps` among them (§581). The image registry module's `holds`, `windows` and `mirrors` go back from their template entries to its old `state` section under the names they had - there. All three are empty on the bus (no key was ever written), so nothing is carried. Their template entries, - no longer declared, become orphans that `orphans` lists, removed only by the operator's `drop` (ADR 0595 §4, §5), - as §529 has it for the healers' old stream. + there. The operator, 2026-11-11 about 15:15 CET ("whatever must be done to not end up in a cycle"): at its first + start, before it serves anything, the image registry of G copies every key of its template `holds`, `windows` and + `mirrors` into its old sections, from its own data alone, and says in its journal and its read tool how many keys + it copied of each; a key already in the old section stays. This needs no other part's answer and adds no edge, + so it can close no cycle; holding its delivery until the template state is empty could wait for ever, since the + running registry keeps writing it. The template entries, no longer declared, then become orphans that `orphans` + lists, removed only by the operator's `drop` (ADR 0595 §4, §5), as §529 has it for the healers' old stream. - **The delivery order.** The running image registry, of its build before G, serves `mirror` only once its bindings are composed, and both the bus module's new build and the image registry's own new build ask it for their base images. So the grants part with design 25 *bindings-first-view* §9 is delivered first, alone, and composes the - running registry's three bindings from `list`'s fields (on the branch `design/25-bindings-from-the-list`). Then + running registry's three bindings from `list`'s fields. Then the image registry's and the bus module's builds of G are built through the running registry's `mirror` (edges of kind 3, §569), and delivered by the edges of §49. Once the image registry of G runs, its state is in the old sections, which the grants part creates as it creates every foundation part's, and no binding of a first view is @@ -491,13 +594,16 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he Why: the operator's answer; the list and the order follow ADR 0619 §1 and §3 and §569. How it is checked: unit (manifest validator), both manifests of G's heads accepted with their old sections, and a template entry in either - refused; the catalog's validation at G's heads refuses neither; the delivery plan of G puts the grants part's + refused; the catalog's validation at G's heads refuses neither; whole (image registry), a first start with keys in + its template `mirrors` copies them, says how many, and then serves them; the delivery plan of G puts the grants part's delivery before both builds. - §585 **What the forge module reads from `step-ended`, and the form of its `failed-steps`.** (§39, §475, §487, §581) The forge module's readings where no line spoke, made design lines: - **The fingerprint** of a step is the body's `fingerprint`, the build-input fingerprint, which the runner fills - in every `step-ended`. No field of the step's `result` is read for it, and a `step-ended` without it is written in - the forge module's journal and changes nothing. + in every `step-ended`. No field of the step's `result` is read for it. A `step-ended` without it writes the + forge module's finding `step-unread` (scope the module the step is of, id the run), naming the run, the module and + the step, cleared by a later `step-ended` of that step with its fingerprint, so a failure that could not be + recorded is seen. - **A failure** is a `step-ended` whose `outcome` is `failed` and whose `status` is `required`. An `optional` or `bypass` step's failure, and a `could not run`, record no failure, as ADR 0666 §11 refuses only a failed `required` step. @@ -508,16 +614,16 @@ removed. A note that corrects a line of G-12 or G-14 is written in that line; he repository, read through `mesh-catalog`'s `registrations` and kept current on `registration-changed`. - **A `seen` key** `seen..` holds `{v, commit, module, fingerprints, at}`: `v` the form's version (1), `fingerprints` every build-input fingerprint the runs named for that commit and module, and `at` as §581 - has it. Both key forms of `failed-steps` are defined in the seat `git`'s contract, in the forge module's - contracts package. + has it. Both key forms of `failed-steps` are the forge module's own, defined in its own package, since no other + part reads that state. - **The fill** of `failed-steps` that its read tool shows is counted by the forge module from the keys and value bytes it reads, against the state's cap of 64 MiB, and said as counted, since a bundle's state call gives no fill of the bus's own. Why: the forge module's developer read these where no line spoke (develop, batch 5); each follows §39, §475, §487 and ADR 0666 §11. How it is checked: unit (forge module), a `required` failure recorded and an `optional` one not; - a rerun pass by `operator` clears and one by another caller does not; a `seen` value read in its form; unit (the - seat `git`'s contract), both key forms read. + a rerun pass by `operator` clears and one by another caller does not; a `seen` value read in its form, and both + key forms; a `step-ended` without `fingerprint` writes `step-unread`. - §586 **The client of `reserved` is the seat `mesh-builds`' contract package.** (§381, §478, §582) The forge module's package registry and the image registry module both ask `reserved`. The call is the seat `mesh-builds`' contract, so its client lives where ADR 0573 §1 puts a seat's contract: the package the seat's holder's diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index 2b952a31..852bc05b 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -65,7 +65,6 @@ decisions: - "ADR 0391" - "ADR 0363" - "ADR 0289" - - "ADR 0283" - "ADR 0253" - "ADR 0240" - "ADR 0283"