Design 47 takes in round 2: waits timed from their start, composing held per node by the assigner, stores sized

Round 2 found a wait whose holder stopped could go untimed, a node away holding
others' wants, a new field in the want that an old node-engine cannot read,
stores sized for whole revisions, a full store that never cleared, a ledger
sized without its operations, and a kept build a program could rewrite. The
design now says wait-held at a wait's start with its bound, starts each clock
when the new version's build is done, holds a node's wants in the assigner
until that node's new node-engine runs, stores an unchanged entry once, sizes
the ledger from its operations, clears full on room, keeps only builds from a
run step that runs nothing else, names every change of the checking program's
fingerprint, and makes the switch safe to run twice.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-06 21:23:25 +01:00
parent de7358a2c5
commit ce3fe6aa3b
@@ -436,7 +436,9 @@ ADR 0656.
- **A record open too long is a finding.** The watchman writes the finding `record-open` for a record still open 24
hours after its merge (a delivery of the whole mesh takes under an hour, so a day means something stands still) and
clears it on `record-ended`; the condition keeper raises it (ADR 0564 §2). The finding names the entries still held
or failed, read from the record's own events.
or failed, read from the record's own events. It is written only while no other condition covers the record: while
an entry of it is held by a wait that raised `acting-part-late`, or a store that holds it raised `full`, that
condition already tells the operator why the record is open, and a second one for the same cause would only be noise.
- **A node away that wakes adds its outcomes.** When it wakes and catches up by its wants (*A change's graph
difference is its delivery*, below), its outcomes reach the record through the same consumer as any other, and the
record shows the node no longer away.
@@ -456,14 +458,19 @@ ADR 0656.
- **A record is kept for 90 days by default**, the gate's setting `record-retention`. Ninety days covers every trace
the operator has asked of a past delivery so far, which went back weeks, never months; the setting can keep records
longer. When the retention removes a record, the gate says `freed` for each revision it pinned.
- **Its size, its cap and its growth.** One record of 30 modules on four nodes is about 30 KiB, against the bus's
256 KiB value limit (research 085, Findings 8). At an estimated 20 deliveries a day (a guess from the days of
research 085, not a measurement) the ledger grows about 0.6 MiB a day, about 54 MiB at the default retention; its
cap is 128 MiB.
- **Its size, its cap and its growth.** A record's state of 30 modules on four nodes is about 30 KiB, against the
bus's 256 KiB value limit (research 085, Findings 8). The ledger keeps every operation beside that state: 30 modules
on four nodes with seven outcomes each is about 840 outcome operations a record, at about 0.2 KiB each about 170 KiB,
so a record costs about 200 KiB with its snapshot. At an estimated 20 deliveries a day (a guess from the days of
research 085, not a measurement) the ledger grows about 4 MiB a day, about 360 MiB at the default retention; its cap
is 768 MiB, twice that, so a busier month than estimated still fits. Removing a record at its retention removes its
operations and its snapshot with it, so the ledger's size follows the retention, not the mesh's age.
- **The switch carries today's state in, and a rollback reads it back.** At the new gate's start, `merges`, `turns`
and every group in flight become records, and the new gate keeps `merges` and `turns` current beside the records,
so a gate rolled back reads current state and no approved group is asked again; milestone H (*Milestones*) stops
writing them.
writing them. The switch is safe to run twice: a group in flight becomes a record keyed by its milestone and heads,
and a switch that finds that record already there leaves it as it is, so a roll forward after a rollback makes no
second record and asks nothing again.
- **The forge bridge asks the seat for a group.** It reads a group's members from the seat `mesh-delivery`'s verb
`groups`, never the gate's `merges` state, both to set the group on each merged message and to publish a group's
end again (ADR 0656, which changes ADR 0633 §4 and §5; ADR 0596). Its account holds no grant to read `merges`.
@@ -579,21 +586,30 @@ Findings 11). How each is kept, read and retained is this design's choice under
have said `freed`. The indexer and the assigner remove one only on applying `freed` or on a revision ceasing to be
current, never on a timer and never by reading the gate's state (ADR 0596; *The delivery record*, above). A trace
must read every revision it moved between, and a revision neither pinned nor current is read by nobody.
- **The caps and the growth** (estimates, to be read from the fill tools once the milestone runs). An assignment graph
revision of about 120 modules on four nodes is under the bus's 256 KiB value limit (its unit test), and a catalog
graph revision of about 120 entries about 120 KiB. A record pins the revisions before and after its delivery, and
consecutive records share one, so at an estimated 20 deliveries a day about 20 revisions of each graph a day stay
pinned: about 4 MiB a day of assignment graph and 2.4 MiB of catalog graph, about 360 MiB and 216 MiB at the
records' default retention of 90 days. The caps are 512 MiB for each. Placement-only and capability-only revisions
are pinned by no record and go when they stop being current.
- **An unchanged entry is stored once.** A revision is kept as its list of entries' digests, and each entry as an
object of its own named by its digest, so an entry that two revisions share is stored once; an entry object is
removed when no kept revision lists it. Consecutive revisions differ by the few entries a delivery moves, so this
keeps the growth to what changes. It is chosen over a larger cap because whole revisions would grow with every
revision whatever changed, and a cap would only delay the same fill.
- **The caps and the growth** (estimates, to be read from the fill tools once the milestone runs). A whole assignment
graph revision of about 120 modules on four nodes is under the bus's 256 KiB value limit (its unit test), and a
catalog graph revision of about 120 entries about 120 KiB. A record pins the revisions before and after its
delivery, and consecutive records share one, so at an estimated 20 deliveries a day about 20 revisions of each graph
a day stay pinned. A delivery moving 5 modules on four nodes adds about 20 assignment entries of about 1 KiB and a
list of about 15 KiB, and 5 catalog entries and a list of about 4 KiB: about 0.7 MiB a day of assignment graph and
0.2 MiB of catalog graph, about 63 MiB and 18 MiB at the records' default retention of 90 days. The caps are 512 MiB
for each, many times the steady state. Placement-only and capability-only revisions are pinned by no record and go
when they stop being current.
- **A store filling is said before it is full.** The indexer, the assigner and the gate each say their fill against
its cap in their read tools, and the event `filling` of their seat when a store passes 80 % of its cap; the watchman
writes the finding `store-filling` naming the part and the store, the condition keeper raises it (ADR 0564 §2), and
it clears when the store falls below 70 %. A full cap refuses loudly, naming the part and the store: the indexer
keeps no new revision and says the merge pending with the reason `objects full`, the assigner keeps its last
revision and writes no new want, and the gate opens no new record and holds the merge; each says it as the event
`full` of its seat, which the condition keeper raises as urgent. Nothing is dropped to make room: a removal is only
the rule above, and the setting `record-retention` or a larger cap is the operator's.
`full` of its seat, which the condition keeper raises as urgent. When a removal brings the store back below its cap,
the part says `room` of its seat, takes the refused work up again (the pending merge, the next revision, the held
record) and the condition clears; a condition that never clears would hide the next fill. Nothing is dropped to make
room: a removal is only the rule above, and the setting `record-retention` or a larger cap is the operator's.
- **What happened is not in the graph.** An entry says what is to run and what it waits on; whether it was built,
wanted or runs healthy is said by the parts as their events and kept in the delivery record (ADR 0656).
- **The want stays the one record a node reads.** The assigner writes `want.<node>.<module>` from a trunk
@@ -701,12 +717,17 @@ ADR 0656 leaves how each part is built to this design, under issue 1921; these f
builds the named program at them, runs it in the run step (ADR 0448 §2) with the input the ask carries as a file,
and writes the program's output and its exit on the build module's step key named by the ask's id (ADR 0525 §6). It
is an accepted ask of the seat, not a request verb, because it is a long job pulled where there is capacity, never
a call to a named node. Only the checker's account may publish `run`, and the checker's account reads only the step
keys of its own asks' ids (ADR 0363 §2), so no other part can have a program run or read its verdict. The seat
a call to a named node. Only the checker's account may publish `run`. Every step key of a `run` ask starts with the
asker's name (`checker.<ask id>`), so the checker's read grant is fixed in advance to its own prefix and never
follows ask ids one by one (ADR 0363 §2); no other part can have a program run or read its verdict. The seat
definition of `node-build-go` in `mesh-delivery-contracts` names it; the checking program, the acting parts and the
composer a check builds from a change all run through it (ADR 0656, ADR 0644). A program is built once per
fingerprint: the build is the builder's build of that fingerprint (ADR 0498 §1), kept like any build and reused by
every later ask naming it, so two checks of changes that leave the checking program unchanged ask one build.
every later ask naming it, so two checks of changes that leave the checking program unchanged ask one build. Only a
build made in a run step that runs nothing but the build is kept and reused: a program's own run happens in a
separate run step over a copy, so a program that rewrites its own binary while it runs changes no kept build. A
candidate's build is never delivered to a node: a want names only a trunk revision's fingerprint (*Only a trunk
revision writes wants*), and a candidate's build is kept only for checks.
- **The first tier's own bound.** A check's first tier has 20 minutes for all its builds together, inside the check's
one-hour bound of the state table and the checker's step bound (ADR 0639 §5); an acting part not built within it is
one the first tier cannot build, and the check fails naming it (ADR 0656). The verdict tells two cases apart: `waiting
@@ -720,24 +741,29 @@ ADR 0656 decides the direction and leaves every choice made in working it out to
| Choice | Why |
|---|---|
| Both graphs kept as objects in an `objects` section in the old form, each with a cap of 512 MiB | both parts are the foundation, whose new kept data goes on the old sections so neither waits on the bus module to start (ADR 0619 §1, §2); the caps hold the estimated 90 days with room |
| Both graphs kept as objects in an `objects` section in the old form, each with a cap of 512 MiB | both parts are the foundation, whose new kept data goes on the old sections so neither waits on the bus module to start (ADR 0619 §1, §2); the caps are many times the estimated 90 days |
| An unchanged entry stored once, a revision being a list of its entries' digests | consecutive revisions differ by a few entries, so the stores grow by what changes; a larger cap over whole revisions would only delay the same fill |
| ADR 0603 §2's keys and the wants kept written beside the revisions until milestone H | a reverted indexer and assigner then read current state and send no node back to an older fingerprint, with nothing to convert at a rollback |
| A revision removed when no record pins it and it is not current, pins counted per revision | a trace must read every revision it moved between, and a revision neither pinned nor current is read by nobody; counting pins keeps a revision two records share |
| The indexer and the assigner hear `pinned`, `freed` and `heads-replaced` through durable consumers | an event said while either was down is applied when it comes back, so no revision leaks and none is dropped early |
| Records kept 90 days by default, the gate's setting `record-retention`, the ledger capped at 128 MiB | the traces asked so far went back weeks; 90 days at an estimated 0.6 MiB a day is about 54 MiB |
| Records kept 90 days by default, the gate's setting `record-retention`, the ledger capped at 768 MiB, a record's operations removed with it | the traces asked so far went back weeks; about 840 outcome operations make a record about 200 KiB, so 90 days at an estimated 20 a day is about 360 MiB, and the cap is twice that |
| `record-open` only when no other condition covers the record | the cause is already told once; a second condition for it is noise |
| `full` cleared by `room` when the store falls back below its cap | the refused work is taken up again, and a condition that never clears would hide the next fill |
| The switch into records keyed by milestone and heads, safe to run twice | a roll forward after a rollback makes no second record and asks no group again |
| A store passing 80 % of its cap said as `filling` and raised; a full cap refused as `full`, naming the part | the operator hears of a store filling while there is time, and a full store stops work loudly instead of dropping what a trace needs |
| A candidate's graphs kept apart and dropped on `heads-replaced` | a candidate must never write wants or be read by a running part, and its graphs mean nothing once its heads are gone |
| The verbs `revision`, `entry` and `difference` on the two seats | each half of the view, and the difference, is served by its one owner through its seat, never read from its state |
| The node-engine reads its want and the catalog half, never the assignment half | the want is the one record a node reads; the assignment half would have each node read every node's placements |
| Composing waits per node, held by the assigner, with no new field in the want: a node's node-engine want first, its other wants once its new node-engine runs | a node away holds only itself (ADR 0603 §7); an old node-engine reads its want as it is, so it is reached with no form it cannot read |
| Capability changes said as the node-engine seat's event `capabilities-changed` | the assigner follows an event instead of reading node records across the seam (ADR 0596) |
| The step events, their fields and their subjects, with the node in the subject of every event a node publishes | every outcome reaches the delivery record as the event of the part that did it (research 085, Findings 7), and a node's account can be granted only its own node's subjects (ADR 0363 §2) |
| A first tier of only the changed acting parts, bounded at 20 minutes, its verdict telling `waiting for a build node` from `did not build` | an ordinary change pays nothing more; a bound turns a build that never ends into a named failure, and the two words tell the operator which act frees it |
| The build seat's ask `run`, published only by the checker's account, its step keys read only by it | the program is a long job pulled where capacity is, run in the sandbox every check already uses (ADR 0448 §2), and no other part can have a program run or read its verdict |
| A program built once per fingerprint and reused | a check that leaves the checking program unchanged costs no build |
| The build seat's ask `run`, published only by the checker's account, its step keys starting with the asker's name | the program is a long job pulled where capacity is, run in the sandbox every check already uses (ADR 0448 §2), and no other part can have a program run or read its verdict |
| A program built once per fingerprint and reused, only from a run step that runs nothing but the build; a candidate's build never delivered | a check that leaves the checking program unchanged costs no build, and a program run from a change can never change a kept build or reach a node |
| Every program built from a change run with no network and no bus account | a change not yet merged is not trusted, and the sandbox already keeps the logins outside (ADR 0448 §2, §4) |
| The rule names of the trunk's and the heads' checking programs compared by the running checker, outside the sandbox; a removed, altered, added or widened rule named in the verdict and the Approve ask | the comparison is made from the sources by a part the change did not build, so a program that drops a rule and hides it is still named; a rule widens or narrows what judges or acts on every module, so the operator sees it before approving |
| Every change of the checking program's fingerprint named in the verdict and the Approve ask, with the rule names of the trunk's and the heads' programs compared by the running checker outside the sandbox as its detail | only the fingerprint covers a helper a rule calls; the comparison is made from the sources by a part the change did not build, so a program that drops a rule and hides it is still named; a rule widens or narrows what judges or acts on every module, so the operator sees it before approving |
| A rule matching a module with no holder for its step held by name, and its check failed | a rule nobody serves would otherwise pass in silence |
| Each step's wait held by the part that does the step, with its own read and release tools and the events `wait-held` and `wait-ended` | the part that does the step already holds its state, and the watchman and the record follow every wait by events alone |
| Each step's wait held by the part that does the step, with its own read and release tools and the events `wait-held` (at the wait's start, with its bound) and `wait-ended`, timed by the watchman, whose clock starts when the new version's build is done | the part that does the step already holds its state; the watchman timing from `wait-held` still raises a wait whose holder stopped, and counting from the build done leaves a slow build to the build's own wait |
| The bound per step in the acting rule, with a default per step | the time a step's new version needs is the acting part's to know, not another module's |
| A build module's wait ends on the agreed toolchain naming the new build module | the agreed toolchain is what a fingerprint takes (ADR 0584 §1), so before it names the new build module no module could be built once by it |
| The running indexer makes a catalog graph revision with the named indexer's entry | the newer indexer can then be wanted and granted, so a revision waiting for it is never stuck behind it |
@@ -776,8 +802,8 @@ say another runs a build (ADR 0363 §2); the gate's events are granted to the ga
| an entry wanted | `wanted` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.wanted` | the seat's definition in `mesh-placement-contracts` |
| an entry composed, and running healthy | `composed`, `running` | `node-engine`, the node-engine on each node | `mesh.seat.node-engine.event.composed.<node>`, `.running.<node>` | the seat's definition in `mesh-contracts` |
| a node's capabilities changed | `capabilities-changed` | `node-engine`, the node-engine on that node | `mesh.seat.node-engine.event.capabilities-changed.<node>` | the same |
| a wait held, and ended | `wait-held`, `wait-ended` (step, acting part, its version, the entry, since when; for an end, how: ran, released) | each holder: `mesh-wants` for the assigner, `mesh-grants` for the grants part, `node-engine` for the node-engine (with `.<node>`), the builder's own stream | `mesh.seat.<seat>.event.wait-held`, `.wait-ended`; `mesh.stream.mesh-delivery-builder.event.wait-held`, `.wait-ended` | each seat's definition; the builder's manifest |
| a store filling, or full | `filling`, `full` | `mesh-catalog`, `mesh-wants`, `mesh-delivery` | `mesh.seat.<seat>.event.filling`, `.full` | each seat's definition |
| a wait held, and ended | `wait-held` (step, acting part, its version, the entry, since when, its bound), `wait-ended` (the same, and how: ran, released) | each holder: `mesh-wants` for the assigner (placing, wanting and composing), `mesh-grants` for the grants part, the builder's own stream | `mesh.seat.<seat>.event.wait-held`, `.wait-ended`; `mesh.stream.mesh-delivery-builder.event.wait-held`, `.wait-ended` | each seat's definition; the builder's manifest |
| a store filling, full, and back below its cap | `filling`, `full`, `room` | `mesh-catalog`, `mesh-wants`, `mesh-delivery` | `mesh.seat.<seat>.event.filling`, `.full`, `.room` | each seat's definition |
| a record pins or frees a revision; a candidate's heads replaced; a record ended | `pinned`, `freed`, `heads-replaced`, `record-ended` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.<event>` | the seat's definition in `mesh-delivery-contracts` |
| a milestone's ask asked, withdrawn and answered | `milestone.asked`, `milestone.withdrawn`, `milestone.answered` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.milestone.<milestone>.<event>`, apart from each delivery's `transition` | the same |
@@ -871,9 +897,9 @@ names go, since every new acting part needed code changes elsewhere (research 08
part's version to each version its rule names, and no part names another module in its code to order by (ADR 0656).
- **Within a revision the new version acts first, a wait with a bound.** The entries an acting part acts on wait for
its new version in its own step, with the bound its rule's `bound` gives (ADR 0656), the part's own as ADR 0603 §8
has every wait. When a rule names none, the step's default holds: 60 minutes for building (a build module's own
build and its spread to a majority of build nodes), 30 for composing (a node-engine's apply on every node it runs
on), 10 for granting and placing (a write of one part), and the first tier's 20 for checking. A module changed with
has every wait. A bound's clock starts when the new version's build is done, not when the wait begins, so a slow
build of the acting part is the build's own wait and not counted twice. When a rule names none, the step's default holds: 60 minutes for building (a build module's own
build and its spread to a majority of build nodes), 30 for composing (a node-engine's apply on one node), 10 for granting and placing (a write of one part), and the first tier's 20 for checking. A module changed with
its build module is no longer built with the running recipe and again later, and a build module's edge, a trigger no
want waited on, becomes this wait (ADR 0656, which changes ADR 0603 §1, §3 and ADR 0614 §1, §6).
- **A build module's wait ends on the agreed toolchain.** For building, an entry waits until the agreed toolchain of
@@ -882,10 +908,20 @@ names go, since every new acting part needed code changes elsewhere (research 08
node still on the old build module builds nothing of that language until it reports the agreed one (ADR 0584 §2).
Every other step's wait ends when the acting part's new version runs healthy, as the node-engine seat's `running`
event says.
- **Composing waits per node, by the assigner.** A node's wants wait only for that node's own node-engine, never for
another node's, so a node away holds only itself (ADR 0603 §7). The want gains no field: the assigner, which writes
the wants, holds a node's other wants until the node-engine seat's `running.<node>` says the new node-engine version
runs on that node, and writes that node-engine's own want first. A node-engine of before milestone G thus gets its
own want first and every other want once it runs the new one, so an old node-engine never composes a module it
cannot read. The clock of that node's bound starts when the node is present and its node-engine's want is written,
so a node asleep raises nothing while away.
- **A failed or late new version holds its modules, and a condition names it.** When the new version fails, or its
wait passes its bound, the entries waiting for it stay held, the modules stay on their running builds, and the
holder says `wait-held` past its bound; the watchman writes the finding `acting-part-late` naming the acting part,
its step and since when, and the condition keeper raises it (ADR 0564 §2). An entry moves on only once the new
watchman writes the finding `acting-part-late` naming the acting part, its step and since when, and the condition
keeper raises it (ADR 0564 §2). The holder says `wait-held` at the start of the wait, carrying its bound, and the
watchman times it: a holder stopped in the middle of a wait still has its wait timed, so a stopped holder hides
nothing. At its own start the watchman reads the open waits from each holder's read tool, as it seeds
`take-in-waits` from `indexer_pending`, so a wait said while it was down is timed too. An entry moves on only once the new
version runs, or its holder's release tool releases it; either way the holder says `wait-ended`, and the finding and
the condition clear. The operator sees each stuck acting part as one condition. Going on with the running version
instead would build or check a change with a part it replaced, the cause of the failures of research 085, Findings
@@ -903,8 +939,8 @@ names go, since every new acting part needed code changes elsewhere (research 08
by the indexer it names* has them: a revision is taken in only by the indexer it names, which judges its own change
at its start, and no build waits on which indexer judged it (ADR 0603 §4, as ADR 0633 §1 changes it). So that the
named indexer can start at all, the running indexer makes, for a merged revision naming a newer indexer, a catalog
graph revision holding that indexer's own entry, read from its manifest at the merge, and nothing else of the
revision; the assigner wants it and the grants part grants it like any module, and once it runs it takes the revision
graph revision that is the previous one with only the indexer's entry replaced by the one its manifest at the merge
gives, and nothing else of the revision, so no other want moves before the named indexer takes the revision in; the assigner wants it and the grants part grants it like any module, and once it runs it takes the revision
in whole. Without that, a revision waiting for its indexer would wait for good.
- **A rule with no holder is no silent pass.** An entry a rule matches with no holder for its step is held by the
assigner, naming the missing holder as the hold's reason, and the check fails naming it.
@@ -921,7 +957,7 @@ names go, since every new acting part needed code changes elsewhere (research 08
| building | the builder, on its build key | `builder_waits` | `builder_release` (build the entry with the running build module), beside `builder_build_again` (ADR 0603 §11) |
| placing and wanting | the assigner, on its hold | `assigner_waits` | `assigner_release` (ADR 0603 §11) |
| granting | the grants part | `grants_waits` | `grants_release` |
| composing | the node-engine on each node: the assigner names in a want the node-engine version that want waits for, and the node-engine applies it only once it runs that version, keeping what it runs | `node_waits` | `node_release` |
| composing | the assigner, holding a node's other wants until its new node-engine runs there (*Composing waits per node*) | `assigner_waits` | `assigner_release` |
| checking | the check's first tier | `checker_first_tier` | its ask-again (*The check on the change's own acting parts*) |
Each release writes the hand-act log of the seat `mesh-hand-acts`, with who released and why.
@@ -931,12 +967,17 @@ names go, since every new acting part needed code changes elsewhere (research 08
new build module while one node of three runs it, builds the contracts with the running build module, and once the
second node reports it and the agreed toolchain names it, builds each other module once, by the new one; the third
node builds nothing of the language until it reports the agreed version. Whole (assigner, builder, grants part,
node-engine): a fixture acting part whose new version fails, and one whose wait passes its bound, each leave their
modules on their running builds, say `wait-held`, and raise `acting-part-late` naming it; each holder's release tool
watchman): a fixture acting part whose new version fails, and one whose wait passes its bound, each leave their
modules on their running builds, say `wait-held` at the wait's start with its bound, and raise `acting-part-late`
naming it; a holder stopped in the middle of a wait still raises it, and a watchman restarted mid-wait reads the
wait from the holder's read tool and times it; a bound counts from the new version's build done; each holder's release tool
moves a held entry on, says `wait-ended` and writes the hand-act log, and the condition clears; a wait ends on its
own when the new version runs; each holder's read tool lists its waits with step, acting part, version and since
when; a rule's `bound` overrides the step's default. Whole (indexer, assigner): a merged revision naming a newer
indexer gives a catalog graph revision holding that indexer's entry; the new indexer is wanted and granted, starts,
when; a rule's `bound` overrides the step's default. Whole (assigner, node-engine): during a node-engine change with
one node away, the present nodes take their new node-engine and then their other wants, nothing waits on the node
away and no condition is raised for it; a node asleep since before milestone G wakes, takes its new node-engine
first, then the rest. Whole (indexer, assigner): a merged revision naming a newer indexer gives a catalog graph
revision that is the previous one with only the indexer's entry replaced, and no other want moves; the new indexer is wanted and granted, starts,
and takes the revision in, with no build waiting on which indexer judged it. Whole (assigner): a module with no
holder for its step is held by name, and the check of a change bringing it fails naming the missing holder. Unit
(graph): an acting part acting on itself, two acting parts acting on each other in different steps, and a cycle
@@ -1171,8 +1212,10 @@ running versions never check in their place, and a check fails, naming the part,
invoking a verb no running holder serves, is one reason the first tier cannot run an acting part: the check fails
naming the build module and the verb. An acting part not built within the first tier's bound of 20 minutes is one
it cannot build, and the verdict says `waiting for a build node` or `did not build`.
- **A change to a rule of the checking program is named, by a part it did not build.** Each checking program
declares its rules by name in its source. The running checker, outside the sandbox, reads the rule names from the
- **A change to the checking program is named, by a part it did not build.** Every change of the checking program's
fingerprint is named in the verdict and the Approve ask, whatever changed: a helper a rule calls changes what the rule
judges as much as the rule does, and only the fingerprint covers every input. The rule-name comparison is its detail:
each checking program declares its rules by name in its source. The running checker, outside the sandbox, reads the rule names from the
trunk's sources and from the heads' and compares them; every rule removed, added or altered (its source changed) is
named in the verdict and in the operator's Approve ask. The program built from the change is never asked which rules
it has, so a program that drops a rule and hides it is still named.
@@ -1196,7 +1239,9 @@ running versions never check in their place, and a check fails, naming the part,
builds but does not run fails naming it; a first-tier ask no build node takes within the bound says `waiting for a
build node`, and one that fails says `did not build`; a change that breaks a rule of the checking program fails its
own check; a change that removes a rule passes, and its verdict and the ask name the removal, also when the
program built from the change hides it; a fixture checking program that tries to reach the bus or the network
program built from the change hides it; a change to a helper a rule calls, with no rule renamed, names the checking
program's new fingerprint in the verdict and the ask; a program that rewrites its own binary while it runs changes
no kept build; a fixture checking program that tries to reach the bus or the network
fails; a `run` ask published from an account other than the checker's is refused by the bus, and the checker's
account cannot read another account's step key; a fixture build module invoking a verb no running holder serves
fails naming it and the verb; a change to an ordinary module asks no first-tier build; two ordinary checks with the
@@ -1833,7 +1878,7 @@ From research 085's milestone the seat gains:
| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656) |
| `milestone.asked`, `milestone.withdrawn`, `milestone.answered` | the one Approve ask per milestone (ADR 0493 §9 as ADR 0496 §2 changes it, issue 1783), held until it is answered, or withdrawn by a new head and asked again (ADR 0441 §1); said on `mesh.seat.mesh-delivery.event.milestone.<milestone>.<event>`, apart from each delivery's `transition`, which stays as it is, so a waiter on one member's pull request wakes only on that member's `transition` |
| `pinned`, `freed`, `heads-replaced` | the events by which the indexer and the assigner keep or drop graph revisions, said only from the gate's account (*The delivery record*) |
| `record-ended`, `filling`, `full` | a delivery record ended; the ledger past 80 % of its cap; the ledger full (*The delivery record*) |
| `record-ended`, `filling`, `full`, `room` | a delivery record ended; the ledger past 80 % of its cap; the ledger full; the ledger back below its cap (*The delivery record*) |
**The seats `mesh-catalog` and `mesh-wants`** gain, from research 085's milestone, `revision` on each (the two halves
of the revision view, ADR 0656), and `entry` and `difference` on `mesh-wants` (ADR 0656), with the events of *What
@@ -2062,15 +2107,20 @@ list:
36. **The milestone can be undone, whole.** In a whole-module fixture of the indexer and the assigner, milestone G's
versions take in revisions and write wants, then the versions of before it are started in their place: they read
the per-module keys the new ones kept writing, compute the same wants, and write no want naming a fingerprint
older than the one each node runs; the gate rolled back the same way asks no approved group again.
older than the one each node runs; the gate rolled back the same way asks no approved group again, and rolled
forward again makes no second record. Whole (checker, gate, assigner): a revert of milestone G passes its check on
the checking program built from the revert's own heads, and is delivered.
37. **Nothing is delivered ahead.** Research 085's milestone is delivered with no part delivered ahead of it, read
from its delivery record (ADR 0656, How it is checked).
38. **A newer indexer is reached.** A merged revision naming a newer indexer gives, from the running indexer, a catalog
graph revision holding that indexer's entry; the new indexer is wanted, granted, started, and takes the revision in.
graph revision that is the previous one with only the indexer's entry replaced; no other want moves; the new
indexer is wanted, granted, started, and takes the revision in.
39. **Revisions are kept, and stores say their fill.** A revision pinned by two records stays until both free it, one
neither pinned nor current goes, a `freed` said while its holder was stopped is applied after its restart, a record
past its 90 days is removed and frees its revisions, a store past 80 % raises `store-filling`, and a full one
refuses loudly, naming the part.
past its 90 days is removed with its operations and frees its revisions, an entry two revisions share is stored
once, a store past 80 % raises `store-filling`, a full one refuses loudly, naming the part, and says `room` and
clears once a removal brings it below its cap; after one fixture delivery of 30 modules on four nodes, `gate_records`
reads the ledger's fill, about 200 KiB for the record.
40. **Nobody says what is not theirs.** A `running` event on another node's subject, a `freed` from an account other
than the gate's, and a `run` ask from an account other than the checker's are each refused by the bus.
41. **A checking program is built once and judged from outside.** Two ordinary checks with the same checking program
@@ -2297,6 +2347,8 @@ order inside one milestone, for this one case (ADR 0644).
- The caps' and the growth's numbers are estimates (*Two graphs, one source of truth*); the fill tools read the real
ones once milestone G runs, and a cap is changed in its manifest.
- Whether the builder should hold a seat of its own, so that its waits are a seat's events like the other holders':
a later proposal.
- How long the checker's stream `checks` keeps its events (*A try*, above).
- Who is granted `checker_try` and the take-in hand tools: the operator's grants, outside this design.
- A web view of deliveries beyond the pull request's comment.