Fix batch 5 by its review, so no cycle, lost act or lost pin is left to develop
mesh/check checking 785cd229b310 on 108a56fbc78a
mesh/delivery merged: source.main moved to novox/hq@108a56fbc78a
mesh/check checking 785cd229b310 on 108a56fbc78a
mesh/delivery merged: source.main moved to novox/hq@108a56fbc78a
Writes round 1's musts into the lines they name. A build edge is met by a running holder. An act cut off by a move ends through its failure path. Pins survive a race and a restart, and the gate restates them. Each point's digest is kept on the bus. The words of a failed build move to the assigner's hold. Also writes the operator's answers of 14:55 and 15:15 CET: three automatic rebuilds and then an ask; the image registry copies its template keys at its first start; refused asks kept are a setting. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 `<scope>.<id>.<kind>` (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 (`<module>.<item>`, 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 `<asker>.<id>` 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 `<asker>.<id>` 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
|
||||
<module> at <fingerprint> failed: <the build key's error>`, the error whole, `transport fault:` included (§568). A
|
||||
build that runs reads `the build of <module> at <fingerprint> 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 <module> at <fingerprint> 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 <module> at <fingerprint> failed: <the error>`, the error whole as
|
||||
`build.ended` carries it, `transport fault:` included (§568); a build that runs reads `the build of <module> at
|
||||
<fingerprint> 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.<group>`). 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 `<module>-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.<commit>.<module>` 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
|
||||
|
||||
@@ -65,7 +65,6 @@ decisions:
|
||||
- "ADR 0391"
|
||||
- "ADR 0363"
|
||||
- "ADR 0289"
|
||||
- "ADR 0283"
|
||||
- "ADR 0253"
|
||||
- "ADR 0240"
|
||||
- "ADR 0283"
|
||||
|
||||
Reference in New Issue
Block a user