|
|
|
@@ -0,0 +1,635 @@
|
|
|
|
|
---
|
|
|
|
|
layer: to-be
|
|
|
|
|
status: in-progress
|
|
|
|
|
design: 47
|
|
|
|
|
milestone: G
|
|
|
|
|
updated: 2026-11-11
|
|
|
|
|
decisions:
|
|
|
|
|
- "ADR 0672"
|
|
|
|
|
- "ADR 0668"
|
|
|
|
|
- "ADR 0667"
|
|
|
|
|
- "ADR 0666"
|
|
|
|
|
- "ADR 0664"
|
|
|
|
|
- "ADR 0626"
|
|
|
|
|
- "ADR 0619"
|
|
|
|
|
- "ADR 0596"
|
|
|
|
|
- "ADR 0595"
|
|
|
|
|
- "ADR 0573"
|
|
|
|
|
- "ADR 0564"
|
|
|
|
|
- "ADR 0539"
|
|
|
|
|
- "ADR 0500"
|
|
|
|
|
- "ADR 0429"
|
|
|
|
|
- "ADR 0404"
|
|
|
|
|
- "ADR 0283"
|
|
|
|
|
- "ADR 0259"
|
|
|
|
|
- "ADR 0235"
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# 47, milestone G: develop's fifth batch, and round 3's notes on §516 to §559
|
|
|
|
|
|
|
|
|
|
One file of milestone G's part of [design 47](../47-delivery-from-commit-to-delivered.md); the part opens at
|
|
|
|
|
[G.md](G.md). Its design lines are numbered across every file of the part and cited as "design 47 G §n".
|
|
|
|
|
|
|
|
|
|
*§560 to §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
|
|
|
|
|
|
|
|
|
|
- §560 **Every event of the recovery module says its act, and its caller wakes on its own act.** (§554, §516, §551)
|
|
|
|
|
Every event of the seat `mesh-recovery` carries `act`, the id `snapshot-kept` or `restore-kept` answered with
|
|
|
|
|
`accepted`, or a hand tool's own act id (§566), and `at`, the UTC time it was said in RFC 3339. Each event's other
|
|
|
|
|
fields:
|
|
|
|
|
- `ended.<module>.<kept>`: `module`, `kept`, `form`, `fingerprint`, `snapshot`, `outcome` (`done` or `failed`),
|
|
|
|
|
`error` when failed, and on `done` the point whole (§516).
|
|
|
|
|
- `restore-started.<module>.<kept>`: `module`, `kept`, `form`, `fingerprint` and `snapshot`, said before the
|
|
|
|
|
restore reads the dump's item events and before any aside is taken (§563).
|
|
|
|
|
- `restored.<module>.<kept>`: `module`, `kept`, `form`, `fingerprint`, `snapshot`, `outcome`, `error`, `aside` and
|
|
|
|
|
`missing` (§516).
|
|
|
|
|
- `set-aside.<module>.<kept>` and `aside-discarded.<module>.<kept>`: the aside's `id`, `name` and `size`.
|
|
|
|
|
- `point-removed.<module>.<kept>` as §551 has it; and `expiry-ended`, a subject without parts, with `removed`, the
|
|
|
|
|
number of points the run removed, 0 included.
|
|
|
|
|
- **Every act ends with an event under its `act`.** `snapshot-kept` and `recovery_take` end with `ended`;
|
|
|
|
|
`restore-kept`, `recovery_restore` and `recovery_restore_aside` (§566) with `restored`; `recovery_aside` with
|
|
|
|
|
`set-aside`; `recovery_discard_aside` with `aside-discarded`; a run of §551's removal (at the start, once a day,
|
|
|
|
|
at a `snapshot-kept` or by `recovery_expire_points`) with `expiry-ended`, also when it removes nothing;
|
|
|
|
|
`recovery_retry` with the ending event of the act it asks again, under an `act` of its own; `recovery_cancel`
|
|
|
|
|
(§566) with the cancelled act's ending event, `outcome` `failed` and `error` `cancelled`.
|
|
|
|
|
- **An act a restart cut off** (accepted and written in the directory, never ended, §554) is ended at the module's
|
|
|
|
|
start, after the start's repair of asides (§516, §564), with its own ending event under its `act`, `outcome`
|
|
|
|
|
`failed` and `error` `stopped`.
|
|
|
|
|
- **A restore runs** from its `restore-started` until a `restored` with the same `act`; the backup holder reads it
|
|
|
|
|
so (§554, §563).
|
|
|
|
|
- **The caller wakes on its own act.** The node agent told `accepted` wakes only on an `ended` or a `restored`
|
|
|
|
|
whose `act` is the one it was given; a `form` and a `fingerprint` alone wake nothing, since a retried act's old
|
|
|
|
|
`failed` carries the same ones. Told `busy` with the running act's `act`, it wakes on that act's ending event
|
|
|
|
|
and asks again, and its new answer gives the `act` it waits on next.
|
|
|
|
|
- **The 90-minute bound** of the node agent's wait (§554) counts from its first ask for that point or restore,
|
|
|
|
|
every `busy` and every ask again within it. Past it, the node agent's have says the wait and the watchman's
|
|
|
|
|
`delivery-waits` raises it.
|
|
|
|
|
- **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, 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), 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 (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 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,
|
|
|
|
|
§388), written into it in G's change. Why: a finding no one can clear stays open for ever, and the four findings
|
|
|
|
|
of §554 named no scope, id or clearing (round 3, health and events, item 4). How it is checked: whole (recovery
|
|
|
|
|
module, backup holder, the keeper), one whole test writes and then clears each: the points directory filled past
|
|
|
|
|
the mark, then an aside discarded; an act held by a dump past the bound (a setting, §557), then started; a dump
|
|
|
|
|
held past it, then started; an aside left at `removed` with no running act, then restored by
|
|
|
|
|
`recovery_restore_aside`.
|
|
|
|
|
- §562 **The recovery module moves as any module that holds data; its points and asides hold no move.** (§554) The
|
|
|
|
|
operator, 2026-11-11 about 14:15 CET at lead-47's terminal: the recovery module may be moved off its node at any
|
|
|
|
|
time, and the restore points and asides in its directory do not hold the move. A placement that moves it is made as
|
|
|
|
|
for any module that holds data. The assigner asks for the move a person must allow before it writes the want (ADR
|
|
|
|
|
0539 §14). The old node's want is written as removed, and its data item stays on that node's disk, with its copies
|
|
|
|
|
in that node's repository, until the operator answers (ADR 0429 §6). On its new node the recovery module starts
|
|
|
|
|
with an empty directory: `kept-points` lists nothing of the old node's, and a `restore-kept` of a point left there
|
|
|
|
|
answers `missing`. Its `from-backup: yes` then asks the new node's backup holder, which holds no copy of it, so
|
|
|
|
|
`restored` says `failed` with `restore point missing` and §300 holds the revert. The placement verb asks the
|
|
|
|
|
recovery module nothing, and §554 no longer refuses a move while a point younger than 90 days is listed.
|
|
|
|
|
- **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
|
|
|
|
|
and the catalog half's claim, §554), says `item-started`, then reads `mesh-recovery`'s `restore-started` and
|
|
|
|
|
`restored`, last per subject, once more. When a restore runs (§560), it ends the item at once with `item-ended`,
|
|
|
|
|
`outcome` `put-off` and `restore` naming that act, having read nothing of the bus, and starts the item again when
|
|
|
|
|
it hears `restored` with that `act`.
|
|
|
|
|
- **The recovery module** says `restore-started`, then reads the item events of the bus's item. While an
|
|
|
|
|
`item-started` has no `item-ended`, it waits, and takes no aside.
|
|
|
|
|
- **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
|
|
|
|
|
holder ends every item of its own that it said started and never ended, at its start, after its read of §556, with
|
|
|
|
|
`item-ended`, `outcome` `failed` and `error` `stopped`, so no restore waits on a dump that no longer runs. Every
|
|
|
|
|
`item-started` and `item-ended` carries `at`, and `item-ended` its `outcome` (`done`, `failed` or `put-off`).
|
|
|
|
|
- **A put-off dump is that night's backup.** The operator, 2026-11-11 about 14:05 CET at lead-47's terminal: an
|
|
|
|
|
item ended `put-off` is neither a failed night nor a missed one (ADR 0235 §13, §21 hold): the item runs when
|
|
|
|
|
`restored` is heard, and the night is the outcome of that run. The item's last good backup ages as before, so
|
|
|
|
|
`backup-stale` is raised only by its own age rule, never for the put-off itself. `backed-up` (§302) lists a
|
|
|
|
|
put-off item with the restore's `act` and since when; `backup-waits` names it after 60 minutes (§561).
|
|
|
|
|
|
|
|
|
|
Why: §554 left open which side gives way when both start at once (round 3, running and testing, item 1); how a
|
|
|
|
|
put-off item counts is the operator's answer (the knowledge base's review of ADR 0672, R5). How it is checked:
|
|
|
|
|
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)
|
|
|
|
|
- **The settings.** The cap of the data item `points` is the recovery module's setting `points-cap`, 10 GiB unless
|
|
|
|
|
set, and its fill mark the setting `points-filling-mark`, 80 % unless set, both read at its start, as §557 has a
|
|
|
|
|
bound.
|
|
|
|
|
- **Before it writes.** Before it writes anything, `snapshot-kept`, `recovery_take` and the aside of
|
|
|
|
|
`restore-kept` add up the bytes the bus states for each bus object they take. An act that would pass the cap
|
|
|
|
|
ends `failed` with `error` `cap`, naming the cap, that size and the directory's fill, before any aside is taken
|
|
|
|
|
and so before any live object is removed. A `snapshot-kept` that ends so holds the move as a failed backup does
|
|
|
|
|
(§296). A point or an aside that passes the cap while it is taken (the stream grew) ends its act the same way.
|
|
|
|
|
- **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.
|
|
|
|
|
|
|
|
|
|
Why: the cap is a bound a whole test sets (§557), and a restore refused for size after the removal would leave the
|
|
|
|
|
module's data only in an aside (round 3, running and testing, item 2 and its notes). How it is checked: whole
|
|
|
|
|
(recovery module, with small settings), a snapshot passing the cap ends `failed` and holds the move; a restore whose
|
|
|
|
|
aside would pass the cap is refused with the live data untouched; `points-filling` written past the mark (§561); a
|
|
|
|
|
failed point leaves no temporary name; an aside left at `set aside` by a stopped restore is removed at the start.
|
|
|
|
|
- §565 **The grants part reads the restore points' names through `kept-points`, and composes without them until it
|
|
|
|
|
answers.** (§554) The bus names of the recovery module's account include those a restore point names (§554). The
|
|
|
|
|
grants part reads them by its `invokes` of `seat:mesh-recovery.kept-points`, a kept call (ADR 0668 §1). It asks
|
|
|
|
|
again on every `ended` with `outcome` `done`, 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; 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 (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).
|
|
|
|
|
|
|
|
|
|
Why: a module's tools recover what a person may need to recover (*Tools are how a module is spoken to*; round 3,
|
|
|
|
|
seams and tools' notes). How it is checked: whole (recovery module), an aside left at `removed` put back by
|
|
|
|
|
`recovery_restore_aside` and refused while the live names exist; a waiting act cancelled and a running one refused.
|
|
|
|
|
- §567 **A restore point is checked by the digest of its bytes before it is restored.** (§516) A point's record
|
|
|
|
|
carries `digest`, the `sha256:` digest of each bus object's snapshot file, written when the point is renamed whole
|
|
|
|
|
(§516). 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 and record were both altered after it was taken is refused before any aside is taken.
|
|
|
|
|
|
|
|
|
|
## The builder
|
|
|
|
|
|
|
|
|
|
- §568 **A transport fault in the builder's own read ends no build key, and a key's error says which kind of fault it
|
|
|
|
|
is.** (ADR 0664 §1 to §3, ADR 0668 §1 to §3; issue 1612, met again 2026-11-11)
|
|
|
|
|
- **The builder's own reads.** The builder's reads of the build seats' state `steps` (its own step keys, §467,
|
|
|
|
|
§481) are kept calls (ADR 0668 §1, §2). A read that meets a transport fault, of ADR 0664 §1's classes or the
|
|
|
|
|
read's own deadline, changes nothing the builder holds and writes no build key. The builder's watch of `steps`
|
|
|
|
|
takes up again from its last revision once the bus answers, and a read it made at its start is made again then.
|
|
|
|
|
Past five minutes it writes `call-unanswered` (ADR 0668 §3). A build key is written `failed` only from a step
|
|
|
|
|
key's end (ADR 0664 §3).
|
|
|
|
|
- **The words of a failed key.** A step that ended failed because its third run met a transport fault (ADR 0664
|
|
|
|
|
§2) gives the build key an `error` that opens `transport fault:`, then the class (`no responders`, `name lookup`,
|
|
|
|
|
`connection` or `deadline`), the step's seat and node, and the cause of its last run in the build module's words.
|
|
|
|
|
Every other failure keeps its own opening (`not stored:`, `not pinned:`, `version taken:`, `refused kind:` or the
|
|
|
|
|
build's own words) and never opens `transport fault:`. The builder's read tools show the error whole, so a reader
|
|
|
|
|
tells a passing cause from a real one without the journal.
|
|
|
|
|
- **What builds such a key again.** The operator, 2026-11-11 about 14:15 CET at lead-47's terminal, decided by the
|
|
|
|
|
record of the builder's retry (number to come), which changes ADR 0664 §3: a key whose error opens `transport
|
|
|
|
|
fault:` is built again, once, as a new revision of the key (ADR 0429 §18), on the first event after its failure
|
|
|
|
|
that says its cause passed. Two events say so: the node-engine seat's `running.<node>.<module>` saying `running`
|
|
|
|
|
for a module whose catalog graph entry claims the build seat the failed step ran on (§546), on any node; and the
|
|
|
|
|
node-engine's `manifest-reads-changed` of the node the failed step ran on (ADR 0668 §4), which it says when that
|
|
|
|
|
node is heard again. The builder hears both through its consumers of the node-engine seat, read as §556 has it.
|
|
|
|
|
It builds a key again at most once per event heard, and only while the key's newest revision is that failure, so
|
|
|
|
|
one event starts one revision. A revision that fails again on a transport fault is said as every failed key is,
|
|
|
|
|
its error opening `transport fault:` again, and waits for the next such event. 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,
|
|
|
|
|
`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
|
|
|
|
|
event is the operator's answer. How it is checked: unit (builder), a `no responders` read of `steps` leaves the key
|
|
|
|
|
`building` and the next revision ends it; a third-run transport fault's key error opens `transport fault: no
|
|
|
|
|
responders`, and a compile error's does not; a `running` of a holder of that build seat builds the key again once,
|
|
|
|
|
and a second `running` while the new revision builds starts nothing; a node's `manifest-reads-changed` builds again
|
|
|
|
|
a key failed on that node; a key failed on a compile error is not built again on either event; 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
|
|
|
|
|
|
|
|
|
|
- §569 **A cycle with a build edge is refused, whatever its other edges, and a grant composed from a first view is an
|
|
|
|
|
edge.** (§49, §558, §374) The indexer's cycle rule (§49) reads every edge of the six kinds in one graph per watched
|
|
|
|
|
branch. A cycle whose edges are all delivery edges (kinds 2, 4, 5 and 6) is delivered as one step. A cycle with at
|
|
|
|
|
least one build edge (kind 1 or 3) is refused, whatever its other edges, at the check and at take-in, naming every
|
|
|
|
|
artifact on it in order with each edge's kind and the manifest word it came from: no build on such a cycle can
|
|
|
|
|
start before a delivery that waits for that build (ADR 0666 §14). Two kinds of edge enter that graph:
|
|
|
|
|
- **A seat verb a build calls.** When a build module's step calls a seat verb while it builds (the image build
|
|
|
|
|
module calling `container-image-registry`'s `mirror` for a base image), its manifest names it in `invokes`, and
|
|
|
|
|
the indexer derives an edge of kind 3 from that seat's holders to every 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)
|
|
|
|
|
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. 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: 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; the image registry's own image, built through its running holder, is not
|
|
|
|
|
refused.
|
|
|
|
|
|
|
|
|
|
## Asks to the operator
|
|
|
|
|
|
|
|
|
|
- §570 **An ask the router refuses is heard by its asker, and no asker asks for longer than its level allows.**
|
|
|
|
|
(ADR 0259 §3, §4) The kind behind it is an asker left waiting on an ask the router never took (issue 1610; met
|
|
|
|
|
again 2026-11-11, when the bus module's three `nats_recreate` asks were refused at take-in and their tool kept
|
|
|
|
|
saying the operator had not answered).
|
|
|
|
|
- **The router says the refusal.** The router (the holder of `operator-channel`) says every ask it refuses at
|
|
|
|
|
take-in, for each cause ADR 0259 §4 gives as it holds, on that asker's
|
|
|
|
|
`mesh.seat.operator-channel.event.decided.<asker>`. The event carries `outcome` `refused`, the ask's `id`, `why`
|
|
|
|
|
in the router's words and `at`, as an ask's end without a warrant is said (ADR 0259 §3).
|
|
|
|
|
- **Every asker ends its request on it.** mesh-sdk's asker client gives its caller `refused` with `why` as soon as
|
|
|
|
|
it hears it. An asker's tool or act that waits on that ask answers `refused` with why, never "not answered yet";
|
|
|
|
|
the bus module's `nats_recreate` and `nats_drop` are among them.
|
|
|
|
|
- **The lifetime is held where every ask is made.** mesh-sdk's asker client 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). 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`, 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
|
|
|
|
|
|
|
|
|
|
- §571 **A mesh package's entry in a lock file is written by the build, never by hand.** (ADR 0573 §2, §4; §47,
|
|
|
|
|
§478, §504) A module's `package.json` names a mesh package (one whose name §504 maps to a catalog artifact) with the
|
|
|
|
|
range `*`, or with the oldest version it accepts (ADR 0573 §2). Its committed `package-lock.json` may hold any entry
|
|
|
|
|
for that package, or none.
|
|
|
|
|
- **What the build writes.** The Node and TypeScript build module removes every entry of a mesh package from the
|
|
|
|
|
workspace's lock before it installs. It then writes the entry from the version the planner read and the
|
|
|
|
|
integrity the package registry gives for that version (ADR 0573 §2). In a delivery group's check where a member
|
|
|
|
|
publishes the package, it writes the entry from that member's head, packed for the check, with the packed
|
|
|
|
|
tarball's integrity (ADR 0573 §4, ADR 0500 §4). Every other entry is installed as the lock names it and checked
|
|
|
|
|
against its integrity (*Every dependency comes by the version its lock file names*).
|
|
|
|
|
- **Before its first publication.** A package of which no version is published yet (`npm-package-registry`'s
|
|
|
|
|
`versions` answers none, as for the packages G itself publishes first) is reached only in a group's check, from
|
|
|
|
|
its member's head. Outside a group, the edge of kind 1 (§47) builds the dependent after the package's first
|
|
|
|
|
publication, which G's delivery orders.
|
|
|
|
|
- **The check's lock rule** never refuses a mesh package's entry, nor its absence.
|
|
|
|
|
- **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
|
|
|
|
|
module's developer, 2026-11-11). How it is checked: unit (Node build module), a lock naming a local tarball's
|
|
|
|
|
integrity for a mesh package is built with the registry's version and integrity; a lock without the entry is built
|
|
|
|
|
alike; a non-mesh entry with a wrong integrity is refused; whole (G's group check), the forge module's tools are
|
|
|
|
|
built against the head of the member that publishes the package, packed.
|
|
|
|
|
|
|
|
|
|
## The builder's forms
|
|
|
|
|
|
|
|
|
|
- §572 **The builder's process answers a package's name, and its tools ask it.** (§478, §504) The mapping from a
|
|
|
|
|
package's name to its module and artifact is the builder process's own, from its view of the catalog half (§348).
|
|
|
|
|
The seat `mesh-builds`' `reserved`, given `name`, is answered by the builder's process from that view. The
|
|
|
|
|
builder's tools bundle holds no catalog view: `builder_versions`, asked by a name, calls `reserved` (or, for a
|
|
|
|
|
module, `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`.
|
|
|
|
|
- §573 **The build seat's ask carries `version`.** (§370, §458) The ask of each build seat (`node-build-go`,
|
|
|
|
|
`node-build-node` and the image build seat) gains the field `version`: the version `MAJOR.MINOR.PATCH` the builder
|
|
|
|
|
reserved for the build (§369), or empty for a build that reserves none (§371, §373). The field is part of the build
|
|
|
|
|
seats' contract package, beside the ask's other fields. The builder fills it when it asks, and each build module
|
|
|
|
|
reads it and passes it as `MESH_VERSION` (§370), passing none when it is empty, so the build stamps none (§458). A
|
|
|
|
|
build module of before G ignores the field and stamps nothing. Why: §370 named no field (develop question of the
|
|
|
|
|
builder's developer on CT5). How it is checked: unit (build seats' contract), `version` read and an empty one
|
|
|
|
|
read as none; whole (Go and TypeScript build modules), a build asked with a version carries it.
|
|
|
|
|
- §574 **`toolchain-agreed` carries `digests` as a list.** (§472, §495) The event's field `digests` is a list, each
|
|
|
|
|
item `{digest, nodes}`: the `sha256:` digest of the toolchain program and the nodes that reported it at the agreed
|
|
|
|
|
version, sorted by digest, with each item's `nodes` sorted. It replaces the field `digest`, which the event no
|
|
|
|
|
longer carries; 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 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 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
|
|
|
|
|
|
|
|
|
|
- §576 **A bundle reads its tool runner's capability words through one call.** (§488, §545, §559) The tool runner
|
|
|
|
|
answers the runtime call `mesh/capabilities` with `{words}`, the capability words it states (§545). mesh-sdk's
|
|
|
|
|
bundle, in Go and in TypeScript, makes that call once, at its first call that needs a word, and keeps the answer
|
|
|
|
|
for as long as it runs. A bundle lives no longer than the tool runner that started it (its channel is that tool
|
|
|
|
|
runner's standard input and output), so after a restart of the tool runner the bundle is started again and reads
|
|
|
|
|
the words again. A tool runner of before G answers `mesh/capabilities` as an unknown call, which the bundle reads as
|
|
|
|
|
no word stated, so a call of a family G adds fails at once, naming its word and the node (§488). A bundle learns
|
|
|
|
|
its node from `MESH_NODE`, which the tool runner sets in the environment of every bundle it starts, read alike in Go
|
|
|
|
|
and in TypeScript. Why: §488 named no channel (develop question of the mesh-sdk developer). How it is checked: unit
|
|
|
|
|
(tool runner), `mesh/capabilities` answers the stated words; unit (mesh-sdk, both languages), the words read once,
|
|
|
|
|
an unknown-call answer read as none, and the node read from `MESH_NODE`.
|
|
|
|
|
- §577 **A part reaches the kept data its `requires` declares through its binding, never by an old name.** (§529;
|
|
|
|
|
ADR 0595 §1, §9; ADR 0619 §2)
|
|
|
|
|
- **A bundle** reaches it only through the tool runner's `mesh/` calls by its local name (ADR 0595 §9), which the
|
|
|
|
|
tool runner resolves in the module's membership binding.
|
|
|
|
|
- **A process with an account of its own** reaches it by its binding: mesh-sdk's process package gains `Binding`,
|
|
|
|
|
which takes a local name and gives the entry's bus names (bucket, stream and subjects, object stores). It asks the
|
|
|
|
|
seat `mesh-broker`'s `show` for the module and the local name at the process's start, as a kept call (ADR 0668 §1)
|
|
|
|
|
that the process's `bus` names in `invokes`. It asks again when it hears `mesh-broker`'s `recreated` or `dropped`
|
|
|
|
|
for that entry, which the process consumes. Until `show` answers, the process acts on nothing that needs the entry
|
|
|
|
|
and its health says it waits, naming the entry (ADR 0283).
|
|
|
|
|
- **The old name** (mesh-sdk's opening of an essential bucket by its declared name) is for a foundation part's old
|
|
|
|
|
`state` section alone (ADR 0619 §2). A part that declares an entry in `requires` opens it by `Binding`; the forge
|
|
|
|
|
bridge's `performed` is among them, moved so in G's change.
|
|
|
|
|
- **The validator's rule for the finding words** (§529) accepts the old stream `<module>-findings` only for a part
|
|
|
|
|
on the foundation list (ADR 0619 §3 as ADR 0666 §4 changes it). It refuses that stream for every other part, whose
|
|
|
|
|
finding stream it requires in the template form: `finding-events`, by the template `events`, its entry listing
|
|
|
|
|
`finding-written.*`, `finding-cleared.*` and `findings-stated.*`.
|
|
|
|
|
|
|
|
|
|
Why: a part's kept data is the bus module's to name and the part's to ask for (ADR 0595 §1, §7), and §529 left the
|
|
|
|
|
run-time path and the validator's row open (develop question of the watchman's developer). How it is checked: unit
|
|
|
|
|
(mesh-sdk's process package), `Binding` answered from `show`, asked again on `recreated`, and waiting while
|
|
|
|
|
unanswered; unit (forge bridge), `performed` opened by its binding; unit (manifest validator), a foundation part's
|
|
|
|
|
old finding stream accepted, another part's refused and its template form accepted.
|
|
|
|
|
|
|
|
|
|
## The indexer's forms
|
|
|
|
|
|
|
|
|
|
- §578 **`manifest-digest` carries its prefix, and the key does not.** (§506, §445) The catalog graph entry's field
|
|
|
|
|
`manifest-digest` is `sha256:` followed by the 64 hex digits, as every digest a record names. The module's value
|
|
|
|
|
and §445's `manifests` key stay the 64 hex digits as on main, since a key's name takes no `:` (§73), and mesh-sdk's
|
|
|
|
|
catalog client gives the bare digits as today. The indexer writes all three from the one digest it computed at take-in.
|
|
|
|
|
Why: one digest in two forms, the field's as the records write a digest and the key's as a key's name allows
|
|
|
|
|
(develop question of the indexer's developer). How it is checked: unit (indexer), the entry's field after its
|
|
|
|
|
prefix equals the key's name and the module's value.
|
|
|
|
|
- §579 **The indexer keeps the records pinning each revision, and removes a revision only on an event.** (§153,
|
|
|
|
|
§178, §216)
|
|
|
|
|
- **Where.** The indexer, a foundation part, keeps its old `state` section `pins`: one key per catalog graph
|
|
|
|
|
revision, `<branch>.<revision>` (the watched branch and the revision's id, each escaped as a key's name is, §73),
|
|
|
|
|
whose value is the list of delivery record ids that pin it. A list of ids is kept rather than a count, so a
|
|
|
|
|
`pinned` heard twice pins once. At about 20 revisions pinned a day for 90 days, about 1,800 keys of about 0.2 KiB
|
|
|
|
|
each, it is capped at 4 MiB (an estimate).
|
|
|
|
|
- **Applying `pinned` and `freed`.** The indexer adds or removes a record's id by compare-and-set, and only then
|
|
|
|
|
acknowledges the event on its durable consumer of `mesh-delivery`'s events (§153). 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, 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 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`. `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`; 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
|
|
|
|
|
|
|
|
|
|
- §580 **Each finding kind belongs to one bundle, and a statement covers only its own kinds.** (§383, §384, §485)
|
|
|
|
|
A module whose bundles write one state `findings` (the forge module has four: its TypeScript tools, its Go package
|
|
|
|
|
registry, its forge and its agent token) gives each finding kind to exactly one bundle. That bundle's mesh-sdk
|
|
|
|
|
writer is made with the kinds it owns, and refuses a `write` or a `clear` of any other kind, naming it. Each bundle
|
|
|
|
|
judges and states its own kinds from its own truth (§384); no bundle reads the whole state to state another's
|
|
|
|
|
findings. Its `findings-stated` carries `kinds`, the kinds it states, and the writer's statement deletes only keys of
|
|
|
|
|
those kinds. The keeper sets its view of that observer, node and set of kinds from the statement, and clears only
|
|
|
|
|
conditions of those kinds that the statement does not name. A statement with no `kinds` (a writer of a part with
|
|
|
|
|
one bundle) covers every kind, as before. Why: a statement that deletes every key it does not state would clear
|
|
|
|
|
another bundle's findings (develop question of the forge module's developer, GIT14). How it is checked: unit
|
|
|
|
|
(writer, both languages), a statement with `kinds` deletes no key of another kind, and a write of another kind is
|
|
|
|
|
refused; whole (keeper), two bundles' statements on one node each keep the other's conditions open.
|
|
|
|
|
- §581 **A `seen` key's 30 days are judged on each step end, with no timer.** (§487) The forge module writes `at` in
|
|
|
|
|
each `seen` key's value, the time of the `step-ended` that wrote it. On each `step-ended` it handles, and at its
|
|
|
|
|
start, it deletes the `seen` keys whose `at` is more than 30 days old, at most 100 for each event, the oldest first,
|
|
|
|
|
so the bus keeps no key time-to-live and no template option is added; 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
|
|
|
|
|
it is checked: unit (forge module), a `seen` key older than 30 days is deleted at the next `step-ended`, and a
|
|
|
|
|
younger one is kept.
|
|
|
|
|
- §582 **`reserved` by name answers a released version from the catalog.** (§381, §478, §369) Given `name` and
|
|
|
|
|
`version` with no reservation of that version (§369 deletes a reservation once its version is released), the
|
|
|
|
|
builder finds the module from the name (§572) and asks the seat `mesh-catalog`'s `versions` for that module, as a
|
|
|
|
|
kept call in its `invokes`. When the catalog holds that version released, `reserved` answers `released` with the
|
|
|
|
|
artifacts and the digests the release recorded, and the registry holder answers its publish as held when the
|
|
|
|
|
digest is the release's, storing nothing (§381). A version neither reserved nor released, or a name no entry
|
|
|
|
|
carries, is answered `error`, naming which, and the holder refuses the publish. Why: the holder knows only the name,
|
|
|
|
|
and the module is the builder's own mapping (develop question of the forge module's developer). How it is checked:
|
|
|
|
|
unit (builder), `reserved` for a released version answers `released` with its digests, and an unknown version
|
|
|
|
|
`error`; whole (registry holder, builder), a publish of a released version at its digest stores nothing.
|
|
|
|
|
- §583 **A manifest's first `version` is §368's.** The `version` G writes into the forge module's and the image
|
|
|
|
|
registry module's manifests is the `MAJOR.MINOR` of the version §372 fills for each, read through
|
|
|
|
|
`builder_versions`, and `0.1` for a module with none published (§368); their old values (`"1"` and `"2"`) carry
|
|
|
|
|
nothing over. Why: §368 answers it (develop questions GIT15 and MAN1). How it is checked: as §368.
|
|
|
|
|
- §584 **The forge module and the image registry module are foundation parts.** (§6, §529, §569; ADR 0619 §1 to §3)
|
|
|
|
|
The operator, 2026-11-11 about 14:30 CET at lead-47's terminal: ADR 0619 §1 is followed as written. The bus
|
|
|
|
|
module's build calls both before the bus module can answer, the forge for its source and the image registry's
|
|
|
|
|
`mirror` for its base image, so both are foundation parts.
|
|
|
|
|
- **The list.** The manifest validator's list (§6, ADR 0619 §3) gains the holder of the seat `git` (the forge
|
|
|
|
|
module, `gitea`) and the holder of `container-image-registry` (the image registry module, `distribution`) in G's
|
|
|
|
|
change.
|
|
|
|
|
- **Their kept data.** Each keeps its kept data in its old sections (ADR 0619 §2): its state `findings` and its
|
|
|
|
|
finding stream `<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. 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. Then
|
|
|
|
|
the image registry's and the bus module's builds of G are built through the running registry's `mirror` (edges of
|
|
|
|
|
kind 3, §569), and delivered by the edges of §49. Once the image registry of G runs, its state is in the old
|
|
|
|
|
sections, which the grants part creates as it creates every foundation part's, and no binding of a first view is
|
|
|
|
|
needed for it.
|
|
|
|
|
|
|
|
|
|
Why: the operator's answer; the list and the order follow ADR 0619 §1 and §3 and §569. How it is checked: unit
|
|
|
|
|
(manifest validator), both manifests of G's heads accepted with their old sections, and a template entry in either
|
|
|
|
|
refused; the catalog's validation at G's heads refuses neither; 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. 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.
|
|
|
|
|
- **A rerun's pass** clears a failure when the body says `rerun: true` and its `by`, the caller the runtime proved
|
|
|
|
|
as the runner's verb `rerun` names its callers, is `operator` or `seat:mesh-healers`; any other `by` clears
|
|
|
|
|
nothing (§39).
|
|
|
|
|
- **The watched branches** are those of each registration whose git origin is a repository the forge holds, per
|
|
|
|
|
repository, read through `mesh-catalog`'s `registrations` and kept current on `registration-changed`.
|
|
|
|
|
- **A `seen` key** `seen.<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 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, 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
|
|
|
|
|
repository publishes, mesh-delivery's contracts package, which defines `mesh-builds` (§52). That package gains a
|
|
|
|
|
`mesh-builds` client with `reserved`, its input and its answers (`reserved`, `released`, `error`), and both
|
|
|
|
|
registry holders use it in place of their own copies. It is not put in mesh-sdk, which keeps no seat-specific
|
|
|
|
|
verb (ADR 0573 §1). Why: code two modules share has one home, and for a seat's verb that home is the seat's
|
|
|
|
|
contract package (develop question of the forge module's developer). How it is checked: unit (mesh-delivery's
|
|
|
|
|
contracts), the client's input and each answer read; the code review finds no second copy in either holder.
|