diff --git a/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md b/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md index 193a80ef..295414cd 100644 --- a/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md +++ b/03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md @@ -19,6 +19,7 @@ decisions: - "ADR 0633" - "ADR 0632" - "ADR 0625" + - "ADR 0621" - "ADR 0619" - "ADR 0618" - "ADR 0615" @@ -618,7 +619,7 @@ module's progress on each node is its want against its have, shown by the view ( ## Two graphs, one source of truth *ADR 0656, which supersedes ADR 0645 to ADR 0648; ADR 0595, -ADR 0596, ADR 0603 §2, §6, §7, ADR 0615 §1, §2, ADR 0567 §5, ADR 0571 §8, ADR 0614 §4, ADR 0618 §2, ADR 0619 §1 to §3, ADR 0636 §1, +ADR 0596, ADR 0603 §2, §6, §7, ADR 0615 §1 to §6, ADR 0621 §1, §2, ADR 0614 §3, ADR 0567 §5, ADR 0571 §8, ADR 0614 §4, ADR 0618 §2, ADR 0619 §1 to §3, ADR 0636 §1, §3, §4, §7, ADR 0639 §7, §8, ADR 0564 §2, ADR 0363 §2.* From research 085's milestone. One graph answered two questions, which modules exist and which version runs where, @@ -708,7 +709,7 @@ Findings 11). How each is kept, read and retained is this design's choice under | Part | Kept data before G (form 0) | After G | Restored at a revert | |---|---|---|---| - | the indexer | the state `modules` (ADR 0603 §2's one key per module) and its durable consumer's position on the forge bridge's stream `forge` | catalog graph revisions as objects, form 1; `modules` no longer declared, nor its tool `indexer_read_modules` or its view, every reader of it reading the catalog half (*Every other reader of which modules exist*, above) | yes: `modules` as it stood at G's delivery, and the consumer's position | + | the indexer | the state `modules` (ADR 0603 §2's one key per module) and its durable consumer's position on the forge bridge's stream `forge` | catalog graph revisions as objects, form 1; `modules` no longer declared, nor its tool `indexer_read_modules` or its view, every reader of it reading the catalog half (*Every other reader of which modules exist*, under *The revision view*, below) | yes: `modules` as it stood at G's delivery, and the consumer's position | | the gate | the states `merges` and `turns` | the delivery record's ledger, form 1; `merges` and `turns` no longer declared | yes: `merges` and `turns` as they stood at G's delivery | | the assigner | the wants, written from the indexer's `modules` | the same wants, form 0, written from the assignment graph; the assignment graph revisions as objects, form 1 | no | @@ -717,7 +718,11 @@ Findings 11). How each is kept, read and retained is this design's choice under fingerprints until the reverted assigner computed them again; leaving them, the nodes keep what runs and the reverted assigner writes them again from the caught-up indexer. The assigner's placement and every other kept data of the three keep form 0 and are never restored, so nothing decided since (a placement, a verdict, the source revision) is - set back. + set back. The restored `modules` is as it stood at G's delivery, so a module added since is missing from it until + the reverted indexer takes in its next source revision, and a reverted reader that learns which modules exist from + `modules` does not name it until then. What a reader granted since stays: the forge module's write grants on a + repository it learnt of from the catalog half are kept, since it grants writes and takes none away, so a revert + costs no repository its account's writes. ### A change's graph difference is its delivery @@ -771,17 +776,37 @@ Findings 11). How each is kept, read and retained is this design's choice under them). - **Every other reader of which modules exist reads the catalog half, in the same milestone.** From milestone G the indexer declares no state `modules` and serves neither its tool `indexer_read_modules` nor a view of it (*Reverted*, - below), so every module that learns from it which modules exist moves in milestone G, not after: the watchman (its + above), so every module that learns from it which modules exist moves in milestone G, not after: the watchman (its manifest findings), the status view (`view_reach`), the forge module (which repositories its account writes), the holders of the seats `mesh-config`, `mesh-provisioner` and `mesh-self-check`, the claude-code module and the tool runner. Each takes a first view of the catalog half by the seat `mesh-catalog`'s verb `revision` and follows its event `catalog-revision`, through `meshcatalogclient` in Go or the seat's generated TypeScript contract in the forge module, names the seat and never the indexer, and keeps no list of its own (ADR 0656 §6, ADR 0596 §2; ADR 0615 §1, - under which every reader of the indexer's state moves in one milestone). The order is the module graph's: each - declares the verb in its `invokes`, so the served-verb edge delivers none of them to a node before an indexer serving - `revision` runs there (ADR 0615 §2, ADR 0614). The design's choice under ADR 0656 §6: one form of the fact, never - `modules` kept beside the catalog graph for these readers, which would be a second source of the same fact. How it is - checked: test 61. + under which every reader of the indexer's state moves in one milestone). A reader that misses a `catalog-revision` + (a gap in the revisions it heard) asks `revision` once more, said in its log (ADR 0615 §5). The design's choice + under ADR 0656 §6: one form of the fact, never `modules` kept beside the catalog graph for these readers, which + would be a second source of the same fact. How it is checked: test 61. + - **The forge module's tools come from mesh-sdk's TypeScript side.** mesh-sdk's TypeScript side serves the proved + caller, a verb's `callers`, a seat's publish, and a view's read and ask-again tools, so the forge module, reading + through the seat's TypeScript contract, answers `_first_views` and `_first_view_again` as the Go + parts do from `go/views`, with no follower or tool of its own. + - **The order is the module graph's, and the foundation's own rule where the graph has no edge.** Six of the + readers declare the verb in their `invokes`, so the served-verb edge delivers none of them to any node before a + build serving `revision` runs (ADR 0615 §2, ADR 0614). Two are left out of the graph's order (ADR 0614 §3, ADR + 0615 §2): the tool runner, a foundation part, and the forge module, on a cycle with the indexer, which reads the + forge through the seat `git`. For both the order is carried by ADR 0615 §4 and §6: the owner answers at once from + what it holds, and a reader started before it waits, saying so. The tool runner asks `revision` of the indexer's + own runtime, never through itself, the tool's `callers` list naming it (ADR 0615 §3, ADR 0621 §1, §2); the forge + module answers the indexer's reads through the seat `git` before it has a first view of its own, so neither waits + on the other. + - **With no view, a reader waits; it never reads "no modules".** All eight follow *A part with no view says it + waits* (below): an empty or missing view is never taken as a mesh with no modules, each says `view-waits` after + five minutes, and once it has a first view it keeps working on what it heard while the indexer is down (ADR 0615 + §6). + - **A candidate's revision moves no reader.** `catalog-revision` says whether it carries a trunk revision or a + candidate's, and a candidate's changes nothing in any of these readers; the forge module grants no write before + its first view and none from a candidate's revision (ADR 0656 §6: a running part acts only on a trunk revision's + view). - **The trunk's heads come from the catalog half.** The checker takes the trunk heads a check merges onto from the catalog half, which carries the heads, and never reads the gate's state `source` (ADR 0596 §2); with the hold and the turns removed it reads nothing of the gate at all (*Nothing merges into a repository under check*). @@ -984,7 +1009,7 @@ on another node's subject is refused by the bus. | Line | The assigner | The indexer | |---|---|---| -| read everything it holds | `assigner_revision` (one assignment graph revision whole, with the revisions of its inputs), `assigner_revisions` (every kept revision with what pins it, and the store's fill against its cap), `assigner_entry` (one entry, with the seat rule that gave each edge), `assigner_difference` (a change's difference), `assigner_waits` (*Order without a person*), `assigner_candidate_graphs` (every candidate's graphs: its heads, its revisions, since when), `assigner_consumer` (its consumer of the gate's events: the last applied, the waiting) | `indexer_catalog_revision` (one catalog graph revision), `indexer_catalog_revisions` (every kept revision with what pins it, and the store's fill against its cap), `indexer_candidate_graphs` (every candidate's catalog half), `indexer_consumer` (the same as the assigner's) | +| read everything it holds | `assigner_revision` (one assignment graph revision whole, with the revisions of its inputs), `assigner_revisions` (every kept revision with what pins it, and the store's fill against its cap), `assigner_entry` (one entry, with the seat rule that gave each edge), `assigner_difference` (a change's difference), `assigner_waits` (*Order without a person*), `assigner_candidate_graphs` (every candidate's graphs: its heads, its revisions, since when), `assigner_consumer` (its consumer of the gate's events: the last applied, the waiting), `assigner_puts` (each put with its step, since when, the `running` that ended it, and whether it stands: past its 30 minutes or older than the trunk's newest build) | `indexer_catalog_revision` (one catalog graph revision), `indexer_catalog_revisions` (every kept revision with what pins it, and the store's fill against its cap), `indexer_candidate_graphs` (every candidate's catalog half), `indexer_consumer` (the same as the assigner's) | | do by hand what it does itself | `assigner_recompute` (compute a revision again from its named inputs and compare it with the one kept) | `indexer_recompute` (compute a catalog graph revision again from its source revision and compare it with the one kept) | | recover without help | `assigner_drop_candidate_graph` (drop a candidate's graphs whose gate event never came), `assigner_release` (*Order without a person*) | `indexer_drop_candidate_graph` (the same for its half) | | one seat, many tools | `mesh-wants`: `revision`, `entry`, `difference` | `mesh-catalog`: `revision`, beside `seat-definitions` | @@ -2054,16 +2079,28 @@ later change of a kept data's form, not as a case of this milestone. ADR 0539 §9). - **A put without hold ends itself once the node runs it.** A put the assigner's tool `assigner_put` writes with `hold` false ends when the node's have runs its fingerprint healthy, and the assigner's rules write the want again - from their next pass (ADR 0546 §5); a held put stays until `assigner_release` (ADR 0546 §6). The assigner hears that - the node runs it as the node-engine seat's `running..` naming the put's fingerprint, through a durable - consumer of its own declared in its `consumes`, the one its composing waits hear too (*Composing waits per node*), - so a put asked while the node was away ends once the node says it runs it; it never reads the node's have across - the seam (ADR 0596 §2). On that event it marks the put ended, says the step as its event `put`, and from then on its - rules own the want; a `running` naming another fingerprint ends nothing. The design's choice under ADR 0546 §5: the - event the node already says is the one fact that ends the put, so a put never pins a want by being forgotten, as - lead-47's puts of a node-engine build did for seven hours on 2026-11-09 (issue 1966), and milestone G's one logged - put of its own indexer ends by itself. Built first in the issue 1966 recovery, kept whole in milestone G. How it is - checked: test 62. + from their next pass (ADR 0546 §5); a held put stays until `assigner_release`, which also ends a put without hold + (ADR 0546 §6). The design's choice under ADR 0546 §5: the fact the node already says ends the put, so a put never + pins a want by being forgotten, as a session's puts of a node-engine build did for seven hours on 2026-11-09 (issue + 1966), and milestone G's one logged put of its own indexer ends by itself. + - **Until milestone G, from the have; from G, from `running`.** The issue 1966 recovery ends the put by the read of + the node's have the assigner already makes, and keeps that end until G. In G the end moves to the node-engine + seat's `running..` naming the put's fingerprint, which milestone G gives that event, heard through a + durable consumer of the assigner's own declared in its `consumes`, the one its composing waits hear too + (*Composing waits per node*); from then it never reads the node's have across the seam (ADR 0596 §2). The order is + an edge of the module graph: the node-engine that says `running` with its fingerprint before the assigner that + ends a put on it. + - **Ended at once when the node already runs it.** At the put's ask, and at the assigner's start for every put not + ended (one written before the upgrade among them), a put ends at once when the last `running` the assigner holds + for that node and module names its fingerprint; a put of the running build pins nothing. A `running` naming + another fingerprint ends nothing. + - **The end is its own step.** The put's end is the step `ended`, written on its key and said as the assigner's + event `put`, carrying the fingerprint and the `running` that ended it (or the release that did). + - **A put that stands is said.** A put not ended 30 minutes after its want was written, or one older than the + trunk's newest build of its module, is said by `assigner_puts` and as the finding `put-stands`, naming the node, + the module and the fingerprint, which the condition keeper raises (ADR 0564 §2); it clears when the put ends. + Thirty minutes is many times a module's start window and the canary's wait, so a put moving normally raises + nothing. How it is checked: test 62. - **The bus module is wanted like any module; only a warranted want replaces its server** (ADR 0594, changing ADR 0539 §14 and §15, ADR 0546 §8 and ADR 0565 §7 for the bus module). The assigner writes the bus module's want by the same rules as every module's, and the node agent applies what that want brings (its files, its processes' bus @@ -2672,13 +2709,23 @@ What milestone G's prepare found, and its test: forge module's writable repositories and the reads of the holders of `mesh-config`, `mesh-provisioner` and `mesh-self-check`, of the claude-code module and of the tool runner each answer from the catalog half: after a merge that adds a module, each names it once `catalog-revision` is said, with no call of the indexer by name; the - indexer's manifest declares no `modules` (each module's existing tests, a row each; live, a search of the catalog's - and mesh-tools' code and manifests finds no `indexer_read_modules` and no view `modules`). + indexer's manifest declares no `modules`; a reader that misses a `catalog-revision` asks `revision` again and then + names the module (in `meshcatalogclient`'s test and the forge module's); a candidate's `catalog-revision` changes + nothing in any reader, and the forge module grants no write before its first view nor from a candidate; the tool + runner started before the indexer says it waits, takes no empty view as no modules, raises `view-waits` after five + minutes, and once it has its first view keeps answering while the indexer is stopped (each module's existing tests, + a row each; whole, tool runner; live, a search of the catalog's and mesh-tools' code and manifests finds no + `indexer_read_modules` and no view `modules`, and after milestone G's first merge that adds a module the status + view's `view_reach` and the watchman's manifest findings name it, as the operator sees them). 62. **A put without hold ends itself.** A put with `hold` false of a fingerprint the node does not run yet ends on that - node's `running` naming its fingerprint, says the step `put`, and the next pass of the rules writes the want, so a - later build is wanted with no `assigner_release`; a `running` of another fingerprint ends nothing; a held put stays - until released; a put whose `running` was said while the assigner was stopped ends after its restart (whole, - assigner; live, `assigner_puts` after milestone G's put of its own indexer). + node's `running` naming its fingerprint, at the step `ended` with the fingerprint and that `running`, and the next + pass of the rules writes the want, so a later build is wanted with no `assigner_release`; a `running` of another + fingerprint ends nothing; a put of the build the node already runs ends at its ask; a put written before the + assigner's start, whose node already runs it, ends at that start; a put whose `running` was said while the + assigner was stopped ends after its restart; a put not ended within 30 minutes, and one older than the trunk's + newest build, is listed as standing by `assigner_puts` and raises `put-stands`, which clears when it ends; + `assigner_release` ends a put without hold; a held put stays until released (whole, assigner; live, + `assigner_puts` after milestone G's put of its own indexer). ## What retires