From da83f29f47378ff4836de855da7a4336d08df730 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 20:22:23 +0100 Subject: [PATCH 01/13] Give milestone G its own part of design 47, and answer its builders' open questions there G's builders read a 443 KB design by searching it and filled what they did not find; playbook 02's shape gives each milestone a part read whole. G's lines move whole into 47-milestones/G.md, numbered as design lines, and the questions G's prepare and develop met within the records are written as the design's own choices (the operator, 2026-11-10: small design choices are the design's, only architectural ones are asked). The size bounds are bent for this split on the lead's word; research 087 takes them up. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../47-delivery-from-commit-to-delivered.md | 1811 +------------- 03-DESIGN/01-to-be/47-milestones/G.md | 2101 +++++++++++++++++ 2 files changed, 2120 insertions(+), 1792 deletions(-) create mode 100644 03-DESIGN/01-to-be/47-milestones/G.md 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 aba7df40..fd70539e 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 @@ -183,8 +183,14 @@ commit on a watched branch runs the module's pipeline steps (lint, tests, compos seat `mesh-pipeline-runner`; only a `required` step that did not pass holds a delivery; the order comes from edges between artifacts; and no Approve is asked per merge, a session merging a pull request through the seat `git`. The checker and its try go. Milestone G builds this with research 085's two graphs as one milestone, and until its -delivery the sections it replaces hold and check and deliver it (ADR 0667 §1, §2). *Pipelines per module*, below, -holds the whole of it. +delivery the sections it replaces hold and check and deliver it (ADR 0667 §1, §2). *Pipelines per module*, in +milestone G's part, holds the whole of it. + +**Every line milestone G builds is in its part,** [47-milestones/G.md](47-milestones/G.md), cited as "design 47 G +§n": the pipelines per module and the two graphs, the delivery record, the acting parts, the check on the change's own +acting parts, the kept data on the bus restored with a delivery, and the verbs and what retires at G's delivery. A +section this file names in italics and does not hold is there, under the same name. A sentence that holds a delivered +line and G's change together stays here. This is the delivery of [to-be 10](10-delivery.md) carried out. To-be 10 says what is built: the comparison of source and artifacts decides, and nothing here changes that. This design says who answers for one @@ -421,661 +427,6 @@ The target (ADR 0547): delivery (ADR 0666 §9, §11; *Pipelines per module*). The trunk is still what runs, on each module's watched branches, which a placement chooses. -## Pipelines per module - -*ADR 0666, ADR 0667; ADR 0595, ADR 0596, ADR 0448 §2, §4, ADR 0548 §5, ADR 0564 §2, §4, ADR 0283, ADR 0656 §4, §6, ADR 0633 §5, -ADR 0664 §4, ADR 0398 §2, ADR 0593 §9; research 086, Direction.* - -From milestone G's delivery. A change of any module, the delivery system's own parts included, goes from a commit to -the machines through that module's **pipeline**: the pipeline steps its **policy** names, run for each commit or group -on each of the module's **watched branches** by the seat `mesh-pipeline-runner`, ordered by the catalog graph's edges -between artifacts, with no hand act (ADR 0666 §1). Milestone G is the one milestone that builds it, beside the two -graphs, the revision view and the delivery record (ADR 0667 §1). Until G's delivery the sections this one replaces -(*Checks*, *A try*, *The Approve and the merge*, the state table and the trunk-only want) hold as written, and they -check and deliver milestone G itself (ADR 0667 §2); each of them says so in a line of its own. - -``` - a session ──merge-pull (seat git)──► forge ──commit on a watched branch──► forge bridge ──► indexer (mesh-catalog) - │ registrations; a catalog graph - │ per watched branch; the revision - │ validated before take-in - ▼ take-in.taken - pipeline runner (mesh-pipeline-runner) ── asks each pipeline step on the serving seat's work queue, hears step-ended: - │ lint, unit tests, integration tests ──► the build seats (node-build-go, node-build-node, node-build-image) - │ composition ──────────────────────────► the assigner (mesh-wants) - │ code review ──────────────────────────► the runner's own verb `review`, from the reviewer's bus account - │ approval ─────────────────────────────► operator-channel: accepted kind ask, then decided - ▼ pipeline-ended (every step's result), keyed by source revision - assigner ──► wants from a revision whose required steps passed, on the watched branch a placement chose - gate ──► the delivery record keeps the step and pipeline events -``` - -### A module's registration - -- **What it holds.** The seat `mesh-catalog` keeps each module's **registration**: its git origin, the path of its - manifest, its watched branches and its policy, on the bus, every version with who changed it and the warrant of the - operator's Approve (ADR 0666 §2). The indexer is a foundation part, so the registrations are kept in its old sections - (ADR 0619 §1 to §3, ADR 0666 §4): one key per module in its old `state` section `registrations`, and every version on - its old `streams` section's stream `registration-history`, kept with no age limit. - The catalog graph is derived from the registrations and the manifests they point to; the mesh knows modules, and a - repository only as a module's git origin. -- **Changing one is a trusted act.** The seat's verbs `register` (a module whole), `policy` (one module's step - statuses, per artifact), `watch` and `unwatch` (add or remove a watched branch) each raise an authorising ask of tier - approve through the seat `operator-channel` (design 46 §10), naming every value the change moves, one line each - ("`mesh-grants:program` unit tests: `required` → `bypass`"), and write the version only on its warrant; a Decline or - an expiry writes nothing (ADR 0666 §2). Until a record binds each forge account to its bus identity, `register` and - `policy` refuse code review as a `required` step, naming why, so no review gates while its author cannot be told - (ADR 0666 §22; issue 1966, note 157). A manifest moved to another path is the one manifest change that changes the - registration; any other manifest change is a commit, judged at its revision. -- **Said and read.** Each written change is said as the seat's event `registration-changed` (the module, the version, - what moved); the verb `registrations` answers every registration, or one with its history. The indexer derives the - modules it keeps from them, and the forge bridge its watched branches, each by a first view of `registrations` and - then that event, never by reading the ledger (ADR 0666 §3, ADR 0596). -- **An unprotected branch is refused.** `register` and `watch` read a branch's protection through the seat `git`'s read - verb `branch` (its head, and whether a rule refuses a direct push and admits only merges), and refuse a watched branch - the forge does not protect, naming it (ADR 0666 §3). -- **The foundation parts are the manifest validator's list.** The manifest validator's list (ADR 0619 §3) holds - exactly the foundation parts, ADR 0619 §1's and the runner, which joins them (ADR 0666 §4); the checker leaves the - list when it is removed (*How milestone G itself is checked and delivered*). A registration holds no foundation - value. How it is checked: a unit test of the manifest validator refuses a template in the manifest of every part on - its list, the runner's included, and judges every other part's manifest as any part's. -- **No part fixes a name.** Every branch, repository and module name a part needs is read from the registrations and the - catalog graph; the lint step refuses such a name in a string, a constant or a table of code logic, a Go import path - not being code logic (ADR 0666 §5). About 39 such names in 9 parts were found on 2026-11-09 (research 086, Findings); - milestone G removes them. -- **The first registrations are one ask.** At its first start on G's build the indexer, holding no registration, - proposes one registration for every module its trunk revision holds, from what it took in (the origin and path of - each), its watched branch the repository's default branch read through the seat `git`, and the statuses the check - judged until then: lint, unit tests, integration tests and composition `required`, code review `optional`, approval - `bypass`; it raises them as one ask naming every registration, as the seat `mesh-module-assigner`'s `adopt` proposes - placement whole (ADR 0567). Until that Approve the runner starts no delivering pipeline and says why, so the - operator is told of this one ask before G's delivery starts. A Decline or an expiry leaves the seed unwritten and - says so; the indexer's tool `indexer_seed_again` raises the ask again, recorded in the hand-act log. -- **Kept for the life of the mesh**, never derived again (research 086, choice 25): the history stream's cap is 64 MiB. - About 120 registrations of about 1 KiB, and an estimated change a week each (a guess, not a measure), grow about 6 MiB - a year, so the cap holds about ten years; past 80 % the indexer says `filling`, as every store of *Two graphs, one - source of truth* does. -- **How it is checked.** Whole (indexer): an unprotected watched branch is refused, naming it; a policy change without - the warrant writes nothing, and a declined one writes nothing; a change from `required` to `bypass` asks, showing - that line; `registration-changed` is said and `registrations` answers the history; the first start proposes the seed - as one ask, and `indexer_seed_again` asks it again after a Decline and after an expiry; a policy making code review - `required` is refused. Unit (the Go build module's lint step): a module name in a string, a constant and a table is - refused, an import path is not. Tests 75, 68 and 83. - -### Watched branches and the catalog graph per branch - -- **Only a watched branch starts a pipeline.** The forge bridge hears every commit of every watched branch the - registrations name, through the seat `git`'s event `branch-moved` (a branch's new head, from the forge's push - webhook, which a merge and an admitted push alike say), and takes the commit from the forge's own record by the seat's - verb `branch`, never from the event's body (ADR 0666 §6). One event for every way a branch moves, because a merge is - not the only one. `branch-moved` is kept on the seat's stream `SEAT_git_branch-moved`, which the bridge hears through - a durable consumer, so a move said while the bridge is down is heard when it starts; at its start it also reads each - watched branch's head the same way. -- **A head with no pipeline is a finding.** A watched branch's head that starts no pipeline within five minutes is the - watchman's finding `pipeline-stands`, naming the branch and its head, raised by the condition keeper (ADR 0564 §2), - unless it waits as progress not owed: a member of a gathered group is governed by ADR 0633 §5's 30-minute bound, and - a merge behind a pending revision of the same branch by its 60-minute bound (ADR 0666 §24). The finding clears on the - runner's `pipeline-started` for that head. -- **The catalog graph per watched branch.** The indexer keeps the catalog graph once per watched branch, from that - branch's manifests at each source revision (ADR 0666 §6): each revision names its branch, and the stores, caps and - pins of *Two graphs, one source of truth* hold per branch. -- **A placement names the branch.** Each module of a placement key names the watched branch its node runs, by default the - registration's first, set by a placement proposal like every other placement change (ADR 0567, ADR 0666 §6). The - assigner computes each node's entries from the catalog graph of the branch its placement names, so a module watched on - `main` and `beta` runs `beta` only where a placement chose it. -- **A source revision per watched branch.** The gate keeps one source revision per watched branch, `source.`, - in place of `source.main` (ADR 0429 §11 to §13 as ADR 0666 §6 changes them), in the old `state` section `source` - already holds, its key's layout being the only change. The gate is no foundation part: the bus module's delivery - waits on no act of the gate's, since the indexer takes a revision in from the forge bridge's stream and the assigner - writes wants from the runner's events, so the gate's other kept data stay in ADR 0595's templates (ADR 0666 §4), and serves it by the seat - `mesh-delivery`'s verb `source` with the branch, saying `source-moved` on each new revision; the indexer and the - node-engine's catch-up read it there, never the gate's state (ADR 0596). Each want names a build-input fingerprint - computed at one such revision. -- **Validated before any take-in, on every branch.** Before it takes a revision in, on every watched branch, the running - indexer asks the manifest validator built from the revision's heads to judge it in the build seat's sandbox (the - ask `run`, *What ADR 0656 leaves to this design*). A revision it refuses stays pending under ADR 0633 §5's - 60-minute bound, its hand acts and its condition `take-in-waits`; the way back is a revert, an ordinary merge - (ADR 0666 §6, §26). A revision on the branch the indexer's own placement chose is taken in only by the indexer it - names (ADR 0633 §1). This validation is no pipeline step and waits on no pipeline, so every delivering pipeline runs - on a revision already taken in. -- **A branch whose protection was removed starts nothing.** On every `branch-moved` the forge bridge reads the - branch's protection again; a move of a watched branch whose protection was removed starts no delivering pipeline, and - the forge bridge writes the finding `branch-unprotected`, naming the branch, which the condition keeper raises and - clears once the protection is back (ADR 0666 §6). -- **A pending revision holds its own branch only.** A merge waits behind a pending revision only on the same watched - branch; other branches move on (ADR 0666 §6, which changes ADR 0633 §3). -- **A refused new indexer holds its branch until a revert lands.** A merge of a new indexer whose own validator refuses - its revision has already landed when it is refused, since the validation runs before take-in and after the merge - (ADR 0666 §6). It stays pending, and every later merge on that watched branch waits behind it, said by - `take-in-waits` at the 60-minute bound, until a revert, an ordinary merge through its pipeline (ADR 0666 §26), lands - and is taken in; nothing takes a later merge in past it. This is the cost of validating every revision with the - validator built from its own heads, the design's reading of ADR 0666 §6, and the operator is told it with the - condition, which names the pending revision and the revert as the way on. -- **How it is checked.** Whole (forge bridge): a commit of a branch no registration names starts nothing; a commit on a - watched branch is taken from the seat's `branch` read, not the event; a head missed while the bridge was stopped is - read at its start. Whole (indexer, assigner): a commit on `beta` gives a revision of the `beta` graph only and moves - only the want of the node whose placement chose `beta`; a revision whose own validator refuses it stays pending and - holds `beta` while `main` moves on. Whole (watchman): a head with no pipeline for five minutes raises - `pipeline-stands`, cleared by `pipeline-started` for that head, and a gathered member does not; a move said while the - bridge was stopped is heard from the seat's stream. Tests 64, 68, 76, 78 and 82. - -### Pipeline steps and the policy - -- **The runner.** The seat `mesh-pipeline-runner`, held once for the mesh by the module `mesh-delivery-pipeline`, - runs each pipeline (ADR 0666 §7). It is a foundation part, since the bus module's own delivery waits on its pipeline - (ADR 0666 §4): its kept data are in old sections (ADR 0619 §2), it waits on no bus module process to start, and it - joins the manifest validator's list of foundation parts, which refuses its manifest asking a template (ADR 0666 §4, - which changes ADR 0619 §1 and §3). It reads the policy from the registrations, asks each step that is not `bypass` of - the seat that serves it, and says each step's start and end and the pipeline's end (*Health and going back*, below). -- **The steps and who serves them.** Lint, unit tests and integration tests are the build seats' (`node-build-go`, - `node-build-node`, `node-build-image`): each gains the verb `steps`, which answers which pipeline steps an artifact - can run by its language's convention (as *The check from release 4* says the tests are found), and the accepted kind - `step` on its stream `check`, which runs one. Composition is the assigner's, an accepted kind `step` of the seat - `mesh-wants`. Code review comes in through the runner's own verb `review`. Approval is asked as the seat - `operator-channel`'s accepted kind `ask`, on its work queue by the runner's name (design 46 §10, *Asks, warrants and answers on the bus*; - the seat has no verb `ask`), and heard on its event `decided` (ADR 0666 §7, §10). A step with nothing to do ends at once as - `nothing to run`, never as passed. -- **The policy.** Each step's status, per module and per artifact, is `required` (runs; holds the delivery until it - passes), `optional` (runs; a failure is shown and may be accepted) or `bypass` (nothing runs; said as bypassed), - kept in the registration and never in a manifest: the manifest validator refuses a manifest that names a status - (ADR 0666 §7; research 086, R3). A `required` step that ends `nothing to run` holds the delivery and names the step. -- **Asked on a work queue, ended by an event.** The runner asks each step on the serving seat's work queue and hears - its end as that seat's event `step-ended` (the record's `step ended`), on - `mesh.seat..event.step-ended.`, the node that ran it in the subject and each holder's account granted only - its own node's subject, through a durable consumer of its own, so a step outlives a restart of either side (ADR 0666 - §7, ADR 0363 §2). A build seat's holder restarted mid-step gets the step again from its work queue and finishes it - (ADR 0664 §1). A unit or integration test step says its test counts in `step-ended`, and `pipeline-ended` names any - repository over 50 integration or 500 unit tests, and any catalog module over them (ADR 0666 §8; the test budget). A step's outcome is one of `passed`, `failed`, `nothing to run`, - `bypassed`, `carried` (with the pipeline it was carried from), `could not run` (naming the part not heard), - `waiting on `, `accepted` (a failed `optional` step a person accepted, with who and why), `superseded` and - `cancelled`. -- **Carried, not run.** A step whose build-input fingerprint and policy did not change is carried by its package - digests, as ADR 0548 §5 holds, the runner making the carry instead of the checker: a test step runs only the packages - with no kept result for their digest, and a run on request's results are reused when the merged commit's inputs match - (ADR 0666 §8; research 086, choice 24). An `approval` step is never carried. -- **Approval comes last.** The runner asks the `approval` step only once every other `required` step of the pipeline - passed, so the operator approves a commit the mesh already judged, the one place ADR 0661 §3 had the check gate the - ask. A failure after an approval is said, as a failed `optional` step is, and the approval stays. -- **Kept on the bus, in old sections.** The runner keeps each pipeline's state in its old `state` section `pipelines`, - one key per pipeline keyed by its watched branch, source revision and module, written by compare-and-set so a holder - replaced mid-pipeline and its successor never both write one; its step log on its old `streams` section's stream - `step-log`; each step's output in its old `objects` section `step-outputs`, named in the log; and each package's - passing result per level in its old `state` section `kept`, under the digest of what its tests read (ADR 0666 §4, §8; - *The runner's kept data*, below). At its first start it takes the checker's kept verdicts into `kept`, read by the - checker's tool `checker_kept`, so the first pipelines after G carry what the checker had judged. -- **How it is checked.** Whole (runner): a step with unchanged fingerprint and policy is carried and says from where, - and an `approval` step is not; a module with no integration tests shows `nothing to run`, and a `required` unit-test - step on a module with no tests holds its delivery; a bypassed step is said; a runner restarted mid-step hears the - step's end and finishes the pipeline; two runners writing one pipeline key, one loses by compare-and-set; a test step's - counts are in `step-ended` and a repository over budget is named in `pipeline-ended`. Whole (Go build module): `steps` - answers the steps of a fixture artifact, a `step` ask ends with `step-ended` on its own node's subject, one on another - node's subject is refused by the bus, and a build module restarted mid-step finishes it. Unit (manifest validator): a manifest naming a status is refused. Tests 65, 66, 73 - and 88. - -### What holds a delivery - -- **The assigner writes a want only from a passed pipeline.** The runner says each pipeline's end as `pipeline-ended` - (the record's `pipeline ended`), keyed by source revision, with every step's result. The assigner consumes it, and - writes a want only from a source revision whose every `required` step passed for the modules it moves, on the watched - branch a placement chose, by compare-and-set on a newer revision, so an older revision ending after a newer one - writes nothing (ADR 0666 §9). The compare-and-set is keyed per module and watched branch: it compares a revision only - with the last one written for the same module on the same watched branch, so a revision of `beta` ending after a - newer one of `main` writes its own want and refuses neither (the design's choice under ADR 0666 §9: source - revisions are kept per watched branch, `source.`, and two branches' revisions have no order between them). This is one more rule beside the assigner's others (ADR 0429 §6), checked per module. - From G's delivery it replaces the trunk-only want (ADR 0656 §2 as ADR 0666 §9 changes it). For a group's source - revision the runner says one `group-ended` once every member's modules have ended, and the assigner writes the - group's wants only on it: a member's own `pipeline-ended` writes nothing (ADR 0666 §9). -- **A failed `optional` step holds nothing.** It is shown in `pipeline-ended`, in the delivery record and in the runner's - `show`; a person may accept it by the runner's verb `accept`, recorded in the hand-act log with who and why. -- **A newer commit supersedes an older pipeline of a module whose fingerprint it changed.** On a newer commit of the - same watched branch the runner ends a module's older running pipeline as `superseded` only when the newer commit - changed that module's build-input fingerprint (ADR 0666 §9); a module the commit leaves as it was finishes its - pipeline. A newer publish of a package likewise supersedes the rebuild pipelines an older publish of it started. A - superseded pipeline's waiting asks are dropped by each build seat's `drop` (ADR 0664 §4), naming - the newer commit as the cause, so a cancelled pipeline leaves no ask on a stream (a gap G's prepare found: a cancelled - build withdrew only the builder's wait). -- **A step nothing starts ends its pipeline.** A step whose prerequisite nothing starts (a seat with no holder, a - prerequisite step `bypass`) ends the pipeline refused within five minutes, naming the step and what it waits for, - never as a silent wait (research 086, R5); a step whose seat has no holder ends its pipeline failed, naming the seat - (ADR 0666 §17). -- **A failed head is said to its author, then to the operator.** A `required` step that failed, or ended `could not - run`, on a watched branch's head is the runner's finding `pipeline-failed`, naming the module, the branch, the commit - and the step; the condition keeper raises it as the commit's author's for two hours and then the operator's, as ADR - 0505 §5 and §6 have a delivery whose cure is its author's new head, and clears it when a newer revision of that branch - passes that step (ADR 0666 §24). The author is the forge's record of the commit, read through the seat `git`. -- **The delivery record keeps every step.** The gate's delivery record consumes the step and pipeline events and keeps - them as long as the record (ADR 0666 §9; *The delivery record*). -- **How it is checked.** Whole (assigner): a failed `required` step writes no want; a failed `optional` step holds - nothing; an older revision's `pipeline-ended` after a newer one's writes nothing, and a revision of one watched branch - ending after a newer one of another writes its own want; a group with one member passed and - another still running writes no want until `group-ended`. Whole (runner): a newer commit that changed a module's - fingerprint supersedes its older pipeline and drops its asks, and one that left it unchanged lets it finish; a step - with no holder ends the pipeline naming the seat within five minutes; a failed `required` step on a head raises - `pipeline-failed`, the author's for two hours and then the operator's, cleared by a newer passing revision. Tests 66, - 67, 87, 89 and 98. - -### Approval is a step, and a session merges a pull request - -- **No Approve per merge.** The operator's approval is the pipeline step `approval`, its status in each module's policy; - no Approve is asked per merge, and the delivery gate's merge turns and its Approve per merge go (ADR 0666 §10, which - changes ADR 0661 §3 to §5). The operator is asked only where a module's policy says `approval`, and for registrations - and policy changes. -- **The approval is an ask of the runner's.** The runner raises an authorising ask of tier approve as the seat - `operator-channel`'s accepted kind `ask`, under its own name, its `about` the module and the watched branch only, so a newer commit's ask - replaces the older one's (design 46 §10). The warrant ends the step passed; an ask that is declined, expires or is - refused ends it `declined`, and one that is replaced ends it `superseded`. Only a declined `approval` step is asked - again, by the runner's verb `ask-again` (ADR 0666 §10). While it waits the runner - says `wait-held` waiting on the operator, never a stall (ADR 0283). -- **A session merges a pull request.** Any session the tool runner proves, and the operator, merges by calling the seat - `git`'s verb `merge-pull` over the mesh, which merges with the forge tools' account (ADR 0398 §2) at the head the - caller names. It judges that this is still the pull request's head and that the branch's protection refuses a direct - push, the protection read again on every `branch-moved` of the branch, and it judges the head's runs by its modules' - policies: it refuses a head when any run at its build-input fingerprint failed a step a policy marks `required`, and - merges a head whose `required` steps passed or that has no run yet, a failed `optional` or a `bypass` step refusing - nothing; it never waits for a run and never asks an Approve (ADR 0666 §11). The forge keeps only its branch protection. -- **Judged from its own state, never by a call.** The holder of the seat `git` consumes the runner's `step-ended` - through a durable consumer of its own and keeps, in its own state `failed-steps`, each failed `required` step by - module and build-input fingerprint, with the commits it was seen at and the run that failed it, until none of those - commits is a watched branch's head or the head an open pull request names (*The runner's kept data*); so a head - merges while the runner is down, judged on what it already heard (ADR 0666 §11, ADR 0596). A head's fingerprints are - those its runs named for its commit; a step that failed at that fingerprint in any run, a group's or another commit's - of the same content included, refuses it, unless a later run of that same step at that fingerprint, asked again by - the operator or the healers (the runner's `rerun`), passed: that pass clears the failure from `failed-steps` (ADR 0666 - §11). The runner's `step-ended` says that the step was a rerun and the caller the runtime proved, and the forge module - clears a failure only for a rerun whose caller is the operator or a healer; a plain pass, or a rerun asked by anyone - else, clears nothing. A run still running is judged by the steps it has ended, since the merge never waits. A refusal names the run, the module and the step, so the session knows what to fix. The gate merges nothing, and its forge merge token is - removed with the checker's buckets and bus account, after the first pipeline after G's delivery has delivered a change - end to end and on the operator's word (ADR 0667 §12; *How milestone G itself is checked and delivered*); the forge bridge hears the landed commit as it hears every commit of a watched - branch. -- **The operator-channel hides no ask.** It keeps design 46's three open asks per asker, never holds the newest back, - and replaces an open ask by the same asker's ask with the same `about` (ADR 0666 §12; design 46 §6). -- **How it is checked.** Whole (forge module, forge bridge): a session's `merge-pull` at its head merges with no run yet, - and with a passed one, and asks no Approve; with the runner stopped a head merges; a head at whose fingerprint any - run failed a `required` step, a head that moved and a branch whose protection was removed since its last move are - refused, each naming why; a failed `optional` step refuses nothing. Whole (runner): an `approval` step is asked last, - a Decline and an expiry each end it `declined` and `ask-again` asks it once more, and a newer commit's ask replaces the - older and ends its step `superseded`. - Live: a declined approval and a fourth ask each reach the operator. Tests 71 and 91. - -### Groups - -- **Formed from the branch name.** The gate forms a group from a shared branch name carrying its issue number, as it - does today without the fixed list of trunk names (ADR 0666 §13). -- **A member reaches its group by landing.** A member has reached its group when its commit lands on its modules' - watched branch. The forge bridge sets the group on each landed member's commit, reading the members from the seat - `mesh-delivery`'s verb `groups`, and publishes `forge.group.merged` when that verb says every member has (ADR 0666 §13, - which changes ADR 0633 §4). -- **One source revision, one pipeline per branch.** The indexer keeps landed members pending until the group's end and - takes them in together as one source revision of that watched branch, saying `take-in.taken`; the runner starts the - group's one pipeline per watched branch on that event, and a member of a gathered group starts no pipeline of its own - (ADR 0666 §13). - When one member's `required` step fails, none of the group's modules is delivered (research 086, R5). -- **A group that never completes is said.** By ADR 0633 §5's 30-minute bound and its condition; `bridge_group_end_again` - says a group's end again from the gate's group verb, never the gate's `merges` state (ADR 0666 §13, which changes ADR - 0633 §5). -- **How it is checked.** Whole (indexer, gate, forge bridge): a group of three repositories, its own packages included, - is one source revision and one pipeline; one member's failed `required` step delivers none; a group with a member that - never lands raises `take-in-waits` at 30 minutes. Tests 67 and 81. - -### Dependencies between artifacts - -- **Six kinds of edge between artifact references.** The catalog graph's edges run between **artifact references**, - `:`, such as `mesh-grants:program`, of six kinds (ADR 0666 §14): - - | Kind | B before A | Derived from | - |---|---|---| - | 1 | B published before A builds | a package A's build source imports (ADR 0573 §3) | - | 2 | B published before A delivers | an artifact A's manifest fetches by reference at its start | - | 3 | B delivered before A builds | a build module A is built by, and the toolchain its agreed version names (ADR 0584 §1) | - | 4 | B delivered before A delivers | a provision A requires and B provides, bus state among them (ADR 0614 §5), and a format's reader (below) | - | 5 | B has acted for A's new version | a seat's acting rule (*What ADR 0656 leaves to this design*): a grant, a credential, a verdict, bus state | - | 6 | B runs a version that serves A | a verb or a manifest word A uses and B serves or reads (ADR 0614 §2, §4), the build seats' `kept-artifact` among them | - - Each kind is derived from what manifests and seat definitions already say, and declared per artifact in the manifest's - artifact entry, its field `needs`, only where nothing else says it. Today's edges run between modules, and only a - package edge names an artifact; at module level the tool runner and the grants part looked like a cycle, per artifact - they are a chain (`mesh-grants:program`, then `node-tools:runtime`, then `mesh-grants:tools`). -- **Removal runs the other way.** When something goes away the order is reversed: a seat moved to a new holder has its - old holder released before the new one claims (ADR 0666 §14). -- **The edges alone order.** The build and the delivery order by these edges alone; a cycle of delivery edges (kinds 2, - 4, 5, 6) is delivered as one step, and a cycle of build edges (kinds 1 and 3) is refused, naming every artifact on it - (ADR 0666 §14; research 086, R17). The cycle rule is the indexer's, in the catalog graph it keeps, so the checking - program's cycle rule goes with the checker. -- **A format's reader first.** A format a running reader cannot read is preceded by its reader, an edge of kind 4 on - every watched branch (ADR 0666 §14): the indexer's manifest validator for a new field of a seat definition or a - manifest, and on each node the node-engine that reads a manifest's new fields before any manifest that writes them - (ADR 0667 §4). -- **How it is checked.** Unit (indexer's graph): a chain per artifact where modules would cycle; two programs needing - each other's new verb are one step; two artifacts each needing the other's new package to build are refused, naming - both; a seat moved to a new holder releases the old first. Live: the tool runner and the grants part delivered in - the order `mesh-grants:program`, `node-tools:runtime`, `mesh-grants:tools`. Test 79. - -### A publish rebuilds - -- **Numbered at publication.** The builder numbers a package's version when it is published, never only when a build - is claimed, and says the publication as the event `package-published` of its seat `mesh-builds` (ADR 0666 §15; design - 51, *The versions*). The seat is new, held once for the mesh, defined in `mesh-delivery-contracts`; the builder held - none until now, and its waits stay on its own stream in this milestone. -- **Every dependent is rebuilt against it.** On `package-published` every artifact with a kind-1 edge to the package is - rebuilt against the new version, and the runner, which consumes the event, starts its pipeline; nothing else is - rebuilt, and a change to tests alone publishes nothing (ADR 0666 §15; research 086, R18). -- **A second start writes nothing.** The runner starts a pipeline on two events, the indexer's `take-in.taken` (ADR - 0666 §13) and `package-published` (ADR 0666 §15). When both reach it for the same source revision and module, the - second start writes nothing: the runner writes a pipeline's key (watched branch, source revision and module, *The - runner's kept data*) by compare-and-set on its absence, so one revision and module has one pipeline. The design's - choice under ADR 0666 §13 and §15: the key the runner already keeps is the one place a start is judged, so neither - event's producer needs to know of the other. -- **Unread packages refuse.** A build that cannot read the published packages refuses and names the unread state - (ADR 0666 §15). -- **How it is checked.** Whole (builder, indexer): a mesh-sdk merge changing one package publishes one version, and only - the artifacts importing that package rebuild; a merge changing only tests rebuilds nothing; a build whose packages - cannot be read refuses naming the state. Whole (runner): a `take-in.taken` and a `package-published` for the same - source revision and module start one pipeline, the second writing nothing. Tests 70, 80 and 101. - -### Any size - -- **A value over the limit is an object.** mesh-sdk's own small object store package (ADR 0595 §9) puts a value over - the bus's per-value limit in the object store beside its state and keeps the object's name in the key; a verb's reply or an event over the - per-message limit carries the object's name (ADR 0666 §16). A replace writes the new object and sets the key by - compare-and-set; the object it replaced stays, as one whose key was deleted does, listed as `key gone` since no key - names it, and only the drop tool removes either, on a person's word: the mesh removes kept data only through `drop` - and `recreate`, each on a person's word (ADR 0595 §4; ADR 0666 §16). -- **One object store per state, granted to its owner.** Each state asked by the template `state` gets its object store - beside it, `-objects`, a template form of ADR 0595 that the bus module provides and the grants part grants to - the state's owner alone (ADR 0666 §16); a foundation part's large values go in an old `objects` section of its own. - Its size is the template's (ADR 0595 §3). An object store holds no space until a value is written to it, so the many - it adds reserve nothing; the few values over the limit (a delivery record, a catalog graph revision list, a test's - output) are estimated, as a guess, at under 1 GiB across the mesh. -- **An object sent is readable by the modules it is for.** A reply's or an event's object is written to the sender's - **sent store**, `_sent-objects`: the bus module plans it, with no manifest field, for every module of the new - form that serves a verb or emits an event, and a foundation part declares it as an old `objects` section - `sent-objects` (ADR 0619 §2). Its cap is the objects template's (ADR 0595 §3), and it keeps an object 7 days, after - which the bus expires it. That age is transport, not kept data: an object sent exists only to carry one reply or one - event, read within its call's bound or its consumer's lag, so its expiry is not a removal of kept data that ADR 0595 - §4 holds to a person's word (the design's choice; 7 days is a guess at the longest a consumer lags, read from the - fill tools once G runs). A full sent store is the bus module's `full` and its condition (ADR 0595 §6), and the sender - then refuses the large reply or event loudly, naming the store. The grants part derives from the manifests a standing grant to read them: the - callers its seat definition names for the verbs it serves, and the consumers whose `consumes` declare its events; - no other module may read them (ADR 0666 §16). The grant follows the manifests, so it is written when they change, - never per object. A consumer's reader of - the form is delivered before its producer writes it, an edge of kind 4. -- **Its tools come with the package.** The package serves each module that imports it `_state_objects` (the - objects of each state, with their size, the key that names each or `key gone`, and the fill against its cap), - `_state_objects_drop` (drop the objects whose key is gone, on a person's word as ADR 0595 §4 removes kept - data), `_sent_objects` (each sent object, its size, when it was sent and to which verb or event, its age - against the store's and the fill against its cap) and `_sent_objects_drop` (drop sent objects older than an - age a person names); each drop names who asked and is recorded in the hand-act log. -- **Its cost.** Only the modules that import the package are built with it, so no other module is rebuilt for it: a - package published before its users build (ADR 0573 §2, ADR 0667 §3). -- **How it is checked.** Whole (mesh-sdk, against the real bus): values of one, ten and a hundred times the per-value - limit stored, sent as a reply and as an event, and read back whole by a caller and a consumer the manifests declare, - and refused to a module that declares neither; a replaced large value leaves its old object listed `key gone` beside - the new one, and a key deleted leaves its object listed `key gone`, each removed by the drop tool and by nothing else; - a sent object is readable within its 7 - days and expired after; each drop without who asked is refused. Tests 69 and 90. - -### The pipeline makes the check, and the checker goes - -- **Every step runs on parts built from the change.** Every pipeline step that runs a change's code runs on parts built - from the change's own heads, in the build seat's sandbox, with no network and no bus account (ADR 0666 §17, ADR 0448 - §2, §4): the composition step composes each machine's declaration with the composer built from the heads and refuses - a change in which two modules own one path; the build seats' steps run lint and tests. A step whose serving part the - change alters runs from the change's build of it, built within 20 minutes or ending `waiting for a build node` or - `did not build`, the first tier of *The check on the change's own acting parts* made the runner's. -- **The run on request.** The runner's verb `run` takes one or more members, each a module at a named commit, runs them - through their pipelines and delivers nothing, replacing the checker's try (ADR 0666 §18). Its callers are the - sessions, as the agents' bus identities the tool runner proves, and the operator, and nothing else, since a run of a - group builds and publishes its packages' tagged versions (design 51). The runner says `run-started`, the indexer builds - the run's candidate from it, and the candidate's composition step writes no want (ADR 0656 §9); the candidate is - dropped on that run's `pipeline-ended`. A step that could not run ends `could not run`, never as passed. A - composition step for a node that sleeps passes it with a warning (ADR 0593 §9). -- **The runner is an acting part in the checker's place.** The acting rule's step `check` becomes the runner's step - `pipeline` (*What ADR 0656 leaves to this design*): its own change runs on its own new version, a failing new runner - holds while the old one runs pipelines (ADR 0656 §4), and the pipelines in flight when its holder is replaced are - taken up again from its kept state (ADR 0666 §19). -- **What the checker did, and where it goes.** - - | The checker's | From G's delivery | - |---|---| - | the merge check, one status `mesh/check` | the pipeline, its result in `pipeline-ended`, the runner's `show` and the delivery record; no status on the forge | - | the try | the run on request | - | the composition check | the composition step, the assigner's | - | the kept verdicts and the carry | the runner's `kept`, taken from the checker's at the runner's first start, and its carry (ADR 0548 §5) | - | the checking program: the manifest validator at the heads, the cycle rule, the composer, the rule names | the indexer's validation before take-in; the indexer's cycle rule; the composition step; `widens` in the pipeline's end | - | the `run` ask, published by the checker's account | published by the runner's account and the indexer's (its validation), each with step keys under its own name | - | its buckets and bus account | removed with the gate's forge merge token after the first pipeline after G's delivery has delivered a change end to end, on the operator's one word, the buckets through `drop` after its snapshot, whose restore is tested first (ADR 0667 §12) | - -- **How it is checked.** Whole (runner, assigner): a run on request of `feature/1966-x` delivers nothing, and a call of - `run` from another account is refused; a composition step refuses two modules owning one path; a step whose part - cannot be heard ends `could not run`; a node asleep passes with a warning; a runner stopped mid-step takes its - pipelines up again. Live: once the checker is removed no module claims the seat `mesh-delivery-checker`. Tests 64, - 84 and 88. - -### Who may say what - -- **Callers per verb, in the seat's definition.** Each seat definition names, per verb, the callers allowed to call it, - in its field `callers` (holder roles, modules, the operator, the healers), which the grants part reads as its one - source of caller limits; the runner's verbs are the first (ADR 0666 §20). A verb whose definition names no `callers` - admits only the seat's holder roles and the operator, so a field left out opens nothing. It replaces the holder's - trusted config value `callers` of the seat `git` and the package seats (design 51), which no holder set for most - verbs. A format its running reader cannot read, so its reader is delivered first (ADR 0667 §4). -- **A step's result from its holder only.** The runner takes a step's result only from the bus account of that step's - seat holder, naming the commit and the build-input fingerprint; a result from another account, or naming another - commit or fingerprint, is refused and said (ADR 0666 §21). -- **A review gates, once it can.** A code review's result is taken through the verb `review` from the reviewer's bus - account and refused from the commit's author, so a review gates where a policy says `required` (ADR 0666 §22, which - changes ADR 0659 §2). Until a record binds each forge account to its bus identity no policy may say it (*A module's - registration*; issue 1966, note 157). -- **A widening is said, never refused.** A change that alters any part serving a step (the runner, step detection, the - lint rule, a build seat's test runner, the composer), or widens an acting rule, a grant or a credential, says so in - its `pipeline-ended`, its field `widens` naming each. The running runner computes it, from the sources of the change - against those of the watched branch, never the runner built from the change, so a change cannot hide its own - widening; nothing refuses it for that (ADR 0666 §23, the operator, 2026-11-10). The teller passes `widens` on to every - channel, as it does `recovery-merged`. This is what replaces ADR 0656 §8's naming in the Approve ask. -- **How it is checked.** Whole (runner, grants part): a forged step result and an author's own review are refused; a - caller not in a verb's `callers`, and any caller but the holder roles and the operator of a verb with no `callers`, - are refused by the bus; a change widening a grant, and one altering the Go build module's test runner, each name it in - `widens`, computed by the running runner, and are delivered. Tests 85 and 86. - -### Health and going back - -- **The runner's events.** Each on `mesh.seat.mesh-pipeline-runner.event....`, - a branch or group name escaped as a key's name is, declared in the seat's definition and published by the runner's - account alone (ADR 0666 §24, ADR 0363 §2): - - | Event | Says | - |---|---| - | `pipeline-started`, `pipeline-ended` | a pipeline's start, and its end with every step's result, its test counts, the source revision and `widens` | - | `group-ended` | a group's one pipeline's end, on which alone its wants are written | - | `run-started` | a run on request's start, with its members at their commits | - | `step-started`, `step-ended` | each step's start and end: the step, its status in the policy, its outcome, its build-input fingerprint, its test counts, for a carried result what it was carried from, and for a rerun that it is one and the caller the runtime proved; a bypassed step too | - | `wait-held`, `wait-ended` | a step's wait: on a person (approval, code review), on a build node, on the revision view | - -- **What a session hears after its merge.** Between its `merge-pull` and the delivery, a session waits with `mesh_wait` - on the runner's `pipeline-started` and `pipeline-ended` of its module, branch and commit, and then on the status - view's `delivered` of the source revision; a failed head reaches it as `pipeline-failed` (*What holds a delivery*). -- **Nothing stands still unseen.** The watchman times each step's owed progress from `wait-held` and `step-started`, - never by run length; a step or a watched branch's head standing still five minutes is its finding `pipeline-stands`, - raised by the condition keeper and cleared on the step's end, or for a head on its `pipeline-started` (ADR 0564 §2, - §4). A step waiting on a person is said as waiting on that person (ADR 0283), never as a stall; a group waiting for - its members, and a merge behind a pending revision, are governed by their own bounds (ADR 0666 §24). At its start the - watchman seeds its open waits from the runner's `stands`, as it seeds `take-in-waits` from `indexer_pending`. -- **The runner's own health.** Its manifest declares its health check, run outside its process (ADR 0240): the runner - answers `show`, and its durable consumers of `step-ended`, `take-in.taken`, `package-published` and `decided` are - each less than five minutes behind; a failed check names which. -- **The runner waits for its view.** With no revision view it says so in `_first_views` and raises `view-waits` - after five minutes, as every part does (ADR 0656 §6). -- **Going back is one act.** A failing new version holds while the old keeps running (ADR 0656 §4); a return to an - earlier build is one value in the placement, the module's `build` in its placement key, set by a placement proposal - (ADR 0666 §24). -- **How it is checked.** Whole (runner, watchman): a step whose worker dies raises `pipeline-stands` within five minutes; - a head with no pipeline raises it and its `pipeline-started` clears it; a step waiting on the operator raises nothing; - a runner restarted mid-step takes its pipelines up again; its health check fails naming a consumer behind. Whole - (assigner): a placement's `build` delivers the earlier build. Tests 76, 77 and 88. - -### A recovery fix is an ordinary merge - -- **No recovery merge verb.** A fix that recovers the mesh is merged like any change on a watched branch, by a - session's `merge-pull`, and runs its pipeline; no trusted recovery merge verb is built (ADR 0666 §26). The shape this - design sketched for one (a verb `merge` of the seat `mesh-delivery` with `recovery-callers`) is dropped. -- **Named a recovery, said everywhere.** Its author names it a recovery by the seat `mesh-delivery`'s verb `recovery` - (the commit and why), never by a pull request's text: the gate marks the delivery record `recovery` with the caller - the runtime proves, and says `recovery-merged`, which the teller passes on to every channel (ADR 0666 §26). A widening - it carries is said in its `pipeline-ended` like any other (*Who may say what*). -- **How it is checked.** Whole (gate): a merge named a recovery is marked in its record and `recovery-merged` is said; - a recovery named by a description is not. Test 92. - -### The tools - -Every new state has a tool that reads it and every new act a tool that starts it by hand (ADR 0666 §25), against the -four lines a module's tools answer; every read tool of a store also answers its fill against its cap: - -| Part (seat) | Read everything it holds | Do by hand what it does itself | Recover without help | -|---|---|---|---| -| the runner (`mesh-pipeline-runner`) | `show` (one pipeline whole, its steps, step log and outputs), `list` (the pipelines of a module, a branch or a commit), `stands` (what stands still, since when), `why-carried` (where a step's result was carried from), `kept` (the kept results by digest) | `start` (a delivering pipeline for a watched branch's head), `run` (the run on request), `rerun` (one step again) | `accept` (a failed `optional` step, with why), `cancel` (a pipeline, dropping its asks), `ask-again` (a declined `approval`) | -| the indexer (`mesh-catalog`) | `registrations` (with each one's history); `indexer_edges` (an artifact's edges per watched branch), `indexer_order` (the order of a change, its cycles named), `indexer_catalog_difference` (two revisions); `indexer_pending` (a group with its missing members) | `register`, `policy`, `watch`, `unwatch` (each on the operator's Approve) | `indexer_seed_again` (the first registrations' ask raised again), `indexer_take_in_pending` (a group taken in as gathered), `indexer_drop_pending` (dropped) | -| the gate (`mesh-delivery`) | `source` with a branch (`gate_source`: every branch's source revision) | — | `gate_source_again` (say `source-moved` again for a branch) | -| the forge module (`git`) | `git_failed_steps` (the failed `required` steps it holds, by module and fingerprint, with the run that failed each) | — | `git_failed_steps_drop` (drop one, with why) | -| the grants part (`mesh-grants`) | `grants_callers` (the callers each verb admits, read from the seat definitions) | — | `grants_why_refused` (for an account and a verb, the grant it lacks and the definition's line that leaves it out) | -| the build seats | `steps` (the steps detected for an artifact) | `steps` with `again` (detect again) | `drop` (ADR 0664 §4) | -| the builder (`mesh-builds`) | `builder_versions` (a package's published versions) | `builder_publish_again` | — | -| the assigner (`mesh-wants`) | `assigner_why_not` (why a module is not on a machine yet: the pipeline it waits for, its hold, the placement, the node's last `running`) | — | `assigner_release` | -| the watchman | `watchman_views` (how far behind each part's view is, from each part's `view-waits` and first-view tools) | — | each part's `_first_view_again` | -| the operator-channel | `asks` (with each asker's asks held back) | — | `ask-again` (offer a held-back ask now) | -| every module that imports mesh-sdk's small object store package | `_state_objects`, `_sent_objects` | — | `_state_objects_drop`, `_sent_objects_drop` | - -Every recover tool writes its act, who and why in the hand-act log of the seat `mesh-hand-acts`. How it is checked: -each tool answers its whole-module test's case, every store's read answers its fill, and `mesh_describe` reads each -(test 93). - -### The runner's kept data, and the build capacity - -Each kept form names its section or template, its retention and its cap; a state template fixes its own size (ADR 0595 -§3), up to 64 MiB. The numbers are estimates, to be read from the fill tools once G runs: an estimated 20 merges a day -(as *The delivery record*), each starting a pipeline for about 3 modules of its repository whose fingerprint it changes, -and an estimated 2 mesh-sdk publishes a day, each rebuilding about 120 artifacts and starting about 120 pipelines: about -300 pipelines a day, of about 6 steps each. - -| Part | Kept data | Section or template | Retention | Cap and growth | -|---|---|---|---|---| -| the runner | `pipelines`: one key per pipeline, keyed by watched branch, source revision and module, its steps, statuses, outcomes, fingerprints and test counts | old `state` section | a pipeline a delivery record cites, deleted with that record (90 days); a superseded pipeline, a run on request's and one no record cites, 7 days after its end; always while it is its branch's newest for its module | 256 MiB; about 300 pipelines a day of about 4 KiB is at most about 105 MiB at 90 days, under half the cap | -| the runner | `step-log`: one entry per step's start and end, its output by name | old `streams` section | by its age, 90 days, as long as the delivery records that cite its steps | 128 MiB; about 3,600 entries a day of about 0.2 KiB is about 63 MiB at 90 days | -| the runner | `step-outputs`: a lint or test step's output | old `objects` section | 7 days | 1 GiB; about 1,200 steps run a day (the rest carried) of about 50 KiB is about 420 MiB at 7 days | -| the runner | `kept`: a package's passing result per level, by digest | old `state` section | 30 days unread | 128 MiB; a publish changes the digest of about 1,000 importing packages at two levels, so about 4,000 entries a day of about 0.3 KiB, about 36 MiB at 30 days | -| the indexer | `registrations` and its stream `registration-history` | old `state` and `streams` sections | the life of the mesh, never derived again | 64 MiB (*A module's registration*) | -| the indexer | catalog graph revisions, per watched branch | objects in the old form (ADR 0619 §2) | while a record pins it or it is its branch's current | 512 MiB, as *Two graphs, one source of truth*; a second watched branch on a few modules adds a few MiB | -| the gate | `source`: one key per watched branch | its old `state` section, as today | 64 revisions of each key; a branch's key deleted when `registration-changed` says the last registration watching it unwatched it (`unwatch`) | a few KiB | -| the forge module | `failed-steps`: failed `required` steps by module and fingerprint, with the commits each was seen at | state | an entry deleted once none of its commits is a watched branch's head or the head an open pull request names, judged on each `branch-moved` and each pull request's close | the template's 64 MiB; an estimated 50 failed steps a day of about 0.5 KiB, most gone within days, is under 1 MiB | -| every module that imports the object store package | `-objects` beside each state | objects | an object no key names, its key deleted or its value replaced, until `_state_objects_drop` on a person's word (ADR 0595 §4, ADR 0666 §16) | the template's; nothing reserved until written | -| every sender of the new form, and a foundation part by its old `objects` section | `_sent-objects` (`sent-objects`) | objects, planned by the bus module with no manifest field | 7 days, transport (*Any size*) | the objects template's cap; full is the bus module's `full` | -| the grants part, for each seat event a definition keeps | `SEAT__`, among them `SEAT_mesh-pipeline-runner_*`, `SEAT__step-ended`, `SEAT_git_branch-moved`, `SEAT_mesh-builds_package-published` and `SEAT_mesh-catalog_registration-changed` | a stream the grants part makes with its durable consumers | the seat definition's age (`keep`), 90 days unless the definition names less | the seat definition's cap (`keep`) | - -**The build capacity.** A pipeline runs a module's steps once per commit on a watched branch, carrying what did not -change, where the check ran before the merge and carried after each trunk move; a group runs one pipeline per watched -branch; a second watched branch runs the steps of the modules it carries once more. A mesh-sdk publish asks about 120 -lint steps and the test steps of the importing packages, about 240 more, the rest carried; a newer publish of the same -package supersedes the older one's rebuild pipelines, so two publishes close together run the rebuilds once. The first -pipelines after G's delivery run every module once, but carry every package whose digest the checker's kept verdicts, -taken into `kept`, already hold, so they run only what changed since. The steps go on the build seats' stream `check`, -after `delivery` and before `recheck`, within each node's build capacity (ADR 0525 §4, §5), and a run on request on the -same stream. How it is checked: live, the build nodes' time per day in the week after G, read from the build modules' -step keys and the builder's build log, against the checker's last week (test 99). - -### How milestone G itself is checked and delivered - -- **Under the clauses as they stand.** Milestone G is checked and delivered under ADR 0656 §5 and the clauses it changed - as they hold before its delivery: the acting parts (the indexer, the build modules, the assigner, the grants part, - the node-engine, and the checker, which makes G's own check) act from the milestone's heads, each first in its step, - in the order of their ordering edges; the trunk-only want, the checker and the Approve per merge as ADR 0661 leaves it - govern this delivery, no exemption is asked and no delivery goes ahead of it (ADR 0667 §2, ADR 0644 §1). -- **Its other parts as any module.** mesh-sdk's small object store package is a package published before the modules - that use it build; the manifest validator moves with the indexer; the runner is placed like any module and acts only - from the next change on; mesh-delivery's `contracts` and `sdk` move under its `modules/` folder (ADR 0667 §3). -- **Readers first** (ADR 0667 §4): an edge of kind 4 each, which the delivery plan derives: the indexer's manifest - validator for the seat definitions' new fields (`callers`, `acts`, `subject`, `keep`) before any definition using - them; on each node the node-engine that reads `form` and `catch-up` before any manifest writing them. -- **The stream maker first** (ADR 0667 §5): the grants part, which makes each kept seat stream and its durable - consumers, before the parts that hear a kept stream. -- **The bus module first** (ADR 0667 §6): the bus module, with `-objects`, the sent store and the grant of each - state's object store to its owner alone, before the modules requiring them, the grants part writing that grant from - the milestone's heads. -- **A bus restart is said first** (ADR 0667 §7): a restart of the bus its new version needs is said to the operator - before it starts. -- **A gap is built, not worked around** (ADR 0667 §8): an order the delivery plan cannot derive from the edges is - built in the delivery system within G, never a step done by hand. -- **A node-engine of the version before applies what it can read** (ADR 0667 §9): it applies every want whose - manifests use no field it does not read, and a want that uses one waits, by the reader edge above, for the new - node-engine on that node. -- **The new assigner and the new merge switch on the first registrations** (ADR 0667 §10). The new assigner, which - writes a want only from a passed pipeline, is delivered before the forge module's new `merge-pull`, which asks no - Approve, and before the runner: an edge of kind 4 from `mesh-delivery-assigner` to each, declared in their manifests' - `needs` since nothing else says it (the design's choice: without it a merge made with no Approve could reach the old - assigner, which writes wants from the trunk with no pipeline). Both switch on one fact, the first registrations - written, said as `registration-changed`: until it the new assigner keeps the trunk-only want and the new - `merge-pull` refuses every merge, naming the missing registrations. -- **Until then, the operator merges a fix at the forge** (ADR 0667 §11). Until that fact an ordinary change waits for - it, and a fix, an indexer fix included, is merged by the operator's own hand at the forge (ADR 0661 §3) and - delivered under the trunk-only want still in force; `indexer_seed_again` asks the first registrations again. Before - the milestone's delivery every merge goes as ADR 0661 has it. -- **What is removed, and when** (ADR 0667 §12). The checker's buckets and bus account and the gate's forge merge token - are removed only after the first pipeline after G's delivery has delivered a change end to end (test 63), the runner - then making every check in the checker's place (ADR 0666 §19), and only on the operator's word, asked once for all - three. The checker then leaves the manifest validator's list of foundation parts (ADR 0666 §4) and is no longer - placed, so no running module declares its buckets and they - are orphans `orphans` lists; they go through ADR 0595's `drop` on that word, after the stream snapshot `drop` takes - (ADR 0595 §4, §5, §10), kept 90 days as the delivery records are; a restore from that snapshot is tested before the - word is asked. The runner is on that list from G's delivery. -- **Told to the operator before it starts**: G's own merges are asked as ADR 0661 has them (ADR 0667 §2, §11); the one - ask of the seed registrations; the bus's restart (ADR 0667 §7); and later the one word for the checker's removal - (ADR 0667 §12). -- **How it is checked.** Tests 94 to 97, and 100 for the assigner's edge. - -### Choices this design makes under ADR 0666 and ADR 0667 - -| Choice | Why | -|---|---| -| The registrations in the indexer's old `state` section with their history on an old stream, capped at 64 MiB, kept for the life of the mesh | the indexer is a foundation part (ADR 0619 §1 to §3, ADR 0666 §4); a registration keeps every version with who and on whose Approve, and nothing derives it again | -| The runner a foundation part, its kept data in old sections, on the manifest validator's list | the bus module's own delivery waits on its pipeline (ADR 0666 §4) | -| The want's compare-and-set keyed per module and watched branch | source revisions are kept per watched branch, so two branches' revisions have no order; comparing across them would let `main` refuse `beta`'s want (ADR 0666 §9) | -| A second start of a pipeline for the same source revision and module, on `take-in.taken` and `package-published`, writes nothing, by the pipeline key's compare-and-set | the runner starts on both events (ADR 0666 §13, §15); the key it already keeps judges a start in one place, so neither producer knows of the other | -| A refused new indexer revision holds later merges on its branch until a revert lands | the validation runs after the merge and before take-in (ADR 0666 §6), so the refused revision has landed; taking later merges in past it would deliver them on a graph its own validator refused | -| Each check at its row's level, every requirement R1 to R18 keeping its named test | the record's rows name the levels; the test budget and one check per release bound what runs | -| The approval asked as the operator-channel's accepted kind `ask` on its work queue | the seat has no verb `ask`: an ask is its accepted kind, by the asker's own name (design 46 §10) | -| The gate no foundation part, its `source` in the old section it already has | the bus module's delivery waits on no act of the gate's; only the key's layout changes | -| The verbs `register`, `policy`, `watch`, `unwatch`, `registrations` and the event `registration-changed` on `mesh-catalog` | one verb per trusted act, each its own ask naming what moves; readers follow the event (ADR 0596) | -| The seed of the first registrations proposed as one ask, with the statuses the check judged until then and approval `bypass`, raised again by `indexer_seed_again` | registering is a trusted act (ADR 0666 §3), and one ask for the whole seed is how placement was adopted (ADR 0567); a declined or expired seed must not leave the pipelines stopped with no way on | -| Code review refused as `required` until forge accounts are bound to bus identities | a review is refused from its author (ADR 0666 §22), which cannot be told until then | -| The seat `git`'s read verb `branch` and event `branch-moved`, kept on its seat stream | registering reads a branch's protection through the seat `git` (ADR 0666 §3); a branch moves by a merge or an admitted push; a stream keeps a move said while the bridge is down | -| The watchman's finding `pipeline-stands` for a step or a head still five minutes, cleared by the step's end or the head's `pipeline-started` | the record's five minutes (ADR 0666 §24), one finding for both, timed from the runner's events | -| The runner's finding `pipeline-failed`, the author's for two hours, then the operator's | ADR 0666 §24 with ADR 0505 §5, §6 | -| A module's watched branch and its `build` in the placement key, set by a proposal | a placement names the branch and a return is one value in it (ADR 0666 §6, §24); every placement change goes by a proposal (ADR 0567) | -| The gate's `source.`, its verb `source` and event `source-moved` | one source revision per watched branch (ADR 0666 §6), read through the seat (G's prepare, the indexer's watch of the gate's `source`) | -| The build seats' verb `steps` and accepted kind `step` on `check`; the assigner's kind `step` on `mesh-wants`; `step-ended` with the node in its subject | steps are asked on the serving seat's work queue (ADR 0666 §7); the `check` stream already orders after `delivery`; a node's account says only its own node's ends (ADR 0363 §2) | -| The outcomes, with `accepted`, `superseded` and `cancelled`; an approval expired or refused ends `declined`, replaced `superseded` | every end a step can have is said as itself (ADR 0666 §10) | -| `approval` asked last, its `about` the module and the branch | the operator approves a commit the mesh already judged, the point ADR 0661 §3 kept in one place; one ask per module and branch, a newer commit's replacing it | -| A superseded pipeline's asks dropped by `drop`, and a pipeline superseded only when its module's fingerprint changed | a cancelled pipeline must leave no ask on a stream (ADR 0664 §4; G's prepare); a module the commit did not change has nothing newer to run (ADR 0666 §9) | -| A group's wants only on `group-ended` | a group is delivered whole or not at all (ADR 0666 §9; research 086, R5) | -| The six kinds derived from existing words, `needs` per artifact only where nothing says it | one form of each fact (ADR 0656 §6); the catalog entry's `needs` already exists from G | -| The cycle rule in the indexer's graph | the graph is the indexer's, and the checking program goes with the checker | -| The builder's seat `mesh-builds` and its event `package-published` | the record has the builder say the publication as an event of its seat (ADR 0666 §15); its waits move only when a later change gives the seat its verbs | -| Each state's object store `-objects` through mesh-sdk's small package, sent objects readable by a standing grant derived from the manifests, and the package's two tools | one object store per state, granted to its owner; the sender's declared callers and consumers read what it sends (ADR 0666 §16); only its users import it, so nothing else is rebuilt | -| The `run` ask published by the runner's account and the indexer's; the verb `run` called by the sessions and the operator only | the steps and the validation run code from the change, in the sandbox only, and each asker reads only its own step keys (ADR 0363 §2); a run publishes tagged packages, so nothing else may ask one | -| The checker's step `check` becomes `pipeline`; the checker's kept verdicts taken into `kept` | the runner is an acting part in the checker's place (ADR 0666 §19); the first pipelines then run only what changed | -| The field `callers` in a seat definition replaces the holders' trusted `callers`, a missing field admitting the holder roles and the operator only | the record makes the definition the grants part's one source (ADR 0666 §20); a field left out must open nothing | -| `widens` in `pipeline-ended`, computed by the running runner over every part serving a step, passed on by the teller | a widening is said and nothing refuses it (ADR 0666 §23, §26); a change must not compute its own | -| `merge-pull` judged from the forge module's own `failed-steps`, heard from `step-ended`, any run at the head's fingerprint counting unless a later rerun of that step passed, the protection read again on every `branch-moved` | the record has it judge from the events it consumes, never by a call, so a head merges while the runner is down (ADR 0666 §11, ADR 0596) | -| The new assigner before the new `merge-pull` and the runner, by `needs`, both switching on the first `registration-changed` | a merge with no Approve must never reach an assigner that writes wants without a pipeline (ADR 0667 §10) | -| The forge bridge's finding `branch-unprotected` | a move of a branch whose protection was removed starts no delivering pipeline and is said (ADR 0666 §6) | -| The verb `recovery` of `mesh-delivery` | a recovery is named by its author as a fact the bus keeps with who said it, never read from text (ADR 0666 §26, research 086 R10) | -| No forge status for a pipeline | the step results live on the bus (research 086 R10), shown by the runner and the delivery record; a status would be a second copy outside it | -| The runner's `pipelines` kept as long as the delivery record that cites each (90 days), a superseded, run-on-request or uncited one 7 days, its step log 90 days by age, its outputs 7, keyed by source revision | a pipeline is read back as long as the record that delivered it; one nothing delivered has no trace to serve after a week; about 300 pipelines a day must fit its caps, so they are 256 MiB and 128 MiB | -| The forge module's `failed-steps` kept until no head it was seen at is a watched branch's head or an open pull request's | an entry judges only a head that can still be merged; once neither names it, nothing can be refused by it | -| The gate's `source.` deleted when the branch's last watch goes | a branch no registration watches makes no revision, so its key would name nothing | -| The sent store kept 7 days by age, with no person's word | an object sent carries one reply or event and is transport, not kept data (ADR 0595 §4); its readers fetch it within a call's bound or a consumer's lag | -| A replaced object listed `key gone`, as a deleted key's is | no key names it, and one listing lets the drop tool take both on a person's word, as ADR 0595 §4 and ADR 0666 §16 have it | -| The checker's removal after the first end-to-end pipeline, on one word, its buckets through `drop`, the snapshot kept 90 days | ADR 0667 §12 asks one word for all three; 90 days is the delivery records' retention, the longest a trace reads back | -| The pipeline steps on the `check` stream after `delivery` | a delivery's build is never held by a pipeline, as a check never held one (ADR 0525 §4) | - ## A delivery *ADR 0239, ADR 0429 §1, ADR 0539 §17.* @@ -1110,182 +461,6 @@ compare-and-set (ADR 0406 §4). It writes a new head when the forge says one, an pull request's verdict is carried to it or a whole check is asked against it (ADR 0548 §5, §7). A running check watches that key and stops only when the head in it moves; a trunk move never ends it (ADR 0548 §1). -### The delivery record - -*ADR 0656; ADR 0595, ADR 0596, ADR 0597, ADR 0603 §7, ADR 0493 §3, §11, ADR 0633 §4, §5, ADR 0441 §1, ADR 0548 §8, -ADR 0564 §2, ADR 0363 §2.* - -From research 085's milestone the gate, holder of the seat `mesh-delivery`, keeps one **delivery record** per -milestone's delivery, a ledger instance (ADR 0595's template `ledger`, ADR 0597) it alone writes (ADR 0656). It -holds: - -- **the group's state**: the heads, the verdicts, the checks and the asks, which `merges` holds today; from milestone - G's delivery the pipelines' `step-started`, `step-ended` and `pipeline-ended` in place of the verdicts, the checks and - the asks, kept as long as the record (ADR 0666 §9); -- **the trace**: the milestone, its issues, its pull requests, their commits, and the graph revisions the delivery - moved between; -- **what happened to each entry on each node**: for every entry of the graph difference, taken in, built, checked, - composed, wanted, running healthy, held (with the hold's reason, so a reader of the record sees why an entry stands - still without asking its holder), failed (with what failed), or away (ADR 0656). - -It holds no tiers, and only the gate orders work by it, so it is no second copy of the keys, the objection research -063 had to a stored plan (research 085, Findings 10). Its fields, retention and tools are this design's choices under -ADR 0656. - -- **The outcomes arrive as events, through a durable consumer of the gate's own.** The gate's manifest `consumes` - each step event of *What each step says* (below), and `wait-held` and `wait-ended` of each holder. The consumer - resumes from the last event applied; an event applied twice changes nothing; an event is applied only when it comes - from its owner's account; and running healthy is taken from the node-engine seat's `running` event, never from a - have read across the seam (ADR 0596). -- **It ends when every present node runs its entries**, and the gate says `record-ended` on the seat `mesh-delivery` - with the record's milestone and its nodes away. It names the nodes away, each with since when, and ends without - waiting for them, as a node away holds nothing (ADR 0603 §7). -- **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. 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. -- **An Approve given holds at its heads, until G's delivery.** An Approve the operator gave for a group's heads holds while those heads - stand: a check of the same heads asked again, after a main move or for any other reason, records its verdict in - the record and asks the operator nothing again (issue 1972). The Approve is withdrawn only as ADR 0441 §1 has it, - when a member stops being mergeable, and a carried verdict is read as current (ADR 0548 §8). From G's delivery no - Approve is asked per merge; an `approval` step's answer is the runner's, kept in the record as its step's end - (ADR 0666 §10). -- **From G's delivery, a record for every landed merge.** A session merges through the seat `git` and the gate merges - nothing, so every merge is one the gate did not make: the gate opens a record for each source revision of a watched - branch the indexer takes in, a group's members in one, and `forge-made` no longer marks a record apart. A merge its - author names a recovery is marked `recovery` (*A recovery fix is an ordinary merge*). -- **A small fix has a record of its own.** A small fix is a milestone of one issue (ADR 0493 §3), so it has a - delivery record like any milestone; nothing is merged outside a released milestone (ADR 0493 §11). -- **It pins the graph revisions it points at.** The gate says, as the seat `mesh-delivery`'s events `pinned` and - `freed`, which catalog and assignment graph revisions a record starts and stops pointing at, and as - `heads-replaced` that a candidate's heads were replaced or its pull request closed. Only the gate's account may - publish them (ADR 0363 §2). The indexer and the assigner hear them through a durable consumer each, declared in - their manifests' `consumes`, which resumes from the last event applied after a restart, so a `freed` said while one - of them was down is still applied; neither reads the gate's state (ADR 0596; *Two graphs, one source of truth*, - below). -- **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.** 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, once.** At the new gate's start, `merges` and every group in flight become - records, and from then on the gate writes `merges` no more; `turns`, which nothing writes while the check gates no - merge, is dropped with the hold (*Nothing merges into a repository under check*). 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 delivering milestone G again after a revert makes no second record and asks - nothing again. A revert of the gate is the delivery system's restore of its kept data (*A module's kept data on the - bus, backed up and restored with its delivery*). -- **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`. -- **What the gate owes the forge is said, not watched, and a merge made at the forge has a record too** (*What the - gate owes the forge*, below). -- **Its tools**, against the four lines a module's tools answer: - - | Line | The gate | - |---|---| - | read everything it holds | `gate_record` (one record whole: the group's state, the trace, each entry's outcome per node, the nodes away), `gate_records` (every record with its fill against the value limit, and the ledger's fill against its cap), `gate_consumer` (its durable consumer: the last event applied per family, the events waiting, the age of the oldest) | - | do by hand what it does itself | `gate_record_repair` (for one record, asks each owner for the outcome of each entry missing one through that owner's read verb: the indexer's `take-in` read, the builder's build read, the checker seat's check read (from G's delivery the runner's `show`), the seat `mesh-wants`'s `entry`, and the node-engine seat's verb `composition` of that node; applies the answers as the consumer would) | - | recover without help | `gate_record_end` (end a record left open, a node gone for good or an entry that never runs, with the reason, recorded in the hand-act log of the seat `mesh-hand-acts`) | - | one seat, many tools | the seat `mesh-delivery`'s verbs, with `groups` answering a group's members and the acts said for it | - -- **How it is checked.** In the gate's whole-module test: the gate stopped and restarted in the middle of a delivery - leaves the record complete with no outcome counted twice; the events `pinned`, `freed`, `heads-replaced` and - `record-ended` are said; a `freed` from an account other than the gate's is refused by the bus; a group approved - before the switch is not asked again after it, nor after milestone G is delivered again following a revert; a record ends once every present node runs its - entries, naming the nodes away; a held and a failed entry are kept with their reason; a record open past 24 hours - raises `record-open`, cleared by `record-ended`; a node away that wakes adds its outcomes and is no longer shown - away; a group's heads approved and checked again ask the operator nothing again; a small fix's merge gets a record - of its own; a record past its retention is removed and frees its revisions; the tools read a record, list the - records with their fill, and a repair completes a record missing an outcome through the owners' read verbs. In the - forge bridge's whole-module test, a group's members are read from `groups` and its end published again from it, and - a read of `merges` by its account is refused. Live: one call of `gate_record` traces an issue of the registry - milestone to each node's running build, also after six later merges; a node away is named with since when and its - outcome taken on waking; a record of 30 modules on four nodes stays under 256 KiB; a group checked again at old - heads after a main move is not asked of the operator again (issue 1972). - -### What the gate owes the forge - -*ADR 0596 §1, §2, ADR 0656 §7, ADR 0437 §8, ADR 0441 §5, ADR 0633 §1, §4, ADR 0363 §2, ADR 0564 §2; from research 085's -milestone, this design's choices under ADR 0596 and ADR 0656 §7.* - -The forge bridge today performs what the gate's states `merges` and `owed` ask (a merge, a status, the delivery's -view, a comment, a line on the commit's note) by watching those keys (ADR 0437 §8), and the forge module writes the -checker's `mesh/check` from a watch of the checker's state `owed`: reads of another module's bus state that ADR 0596 -§2 ends when their owner moves. From research 085's milestone: - -- **Every act is an event, under one id.** The gate says each act it owes as the event `owed` of the seat - `mesh-delivery`, on `mesh.seat.mesh-delivery.event.owed..`, carrying the act, the pull request, - the head and an id; the checker says its status the same way, as the event `owed` of the seat - `mesh-delivery-checker` on `mesh.seat.mesh-delivery-checker.event.owed..`. Only the owner's - account publishes each (ADR 0363 §2). The subject per repository and pull request lets the bridge, a person and a - test read one pull request's acts alone. The seat events are kept on each owner's stream `owed`, 7 days and at most - 64 MiB (an estimated 20 deliveries a day, with about 15 acts of about 1 KiB each, is about 2 MiB over 7 days). -- **Every act is safe to repeat at the forge.** A status and the delivery's view replace what stands; a merge of a - pull request already merged at that head is answered performed; a comment and a line of the note carry the act's - id, and the seat `git`'s verb skips an id the pull request or note already holds. This is chosen over recording the - id before the act: an id recorded and then not performed (the bridge stopped between the two) would be lost, while - an act repeated changes nothing. The bridge's own state `performed`, a key-value state in the new form (ADR 0595), - form 1, one key per id with the outcome and when, kept 7 days as the stream is, its size the one its template fixes - (ADR 0595 §3), is therefore a - saving and a read, never what keeps an act from happening twice. -- **Every outcome is said, and the gate records it.** The bridge says each act's outcome on its own stream `forge` as - the event `owed.ended`, with the id and one of `performed`, `refused` (the forge's own words), `could not be - performed` (the forge did not answer within the call's bound, with the error) or `undecodable` (an act it cannot - read, with why): an act it cannot decode is said, never only logged. The gate hears the stream `forge` through its - durable consumer, writes the outcome into the delivery record, and says an act's id is answered only once an - outcome is recorded. The checker does the same for its own acts: it hears `owed.ended` for its own ids, records each - outcome in its own state (it watches only its own state), says again at its start, under the same id, every act of - its own still unanswered, and lists one unanswered past 15 minutes in its own stalled read. -- **What is unanswered is said again, and stalls by a bound.** At its start the gate says again, under the same id, - every act its records hold unanswered. An act unanswered 15 minutes after it was said is listed by `stalled`, which - the condition keeper raises as the stalled line (ADR 0421 §7, ADR 0564 §2): fifteen minutes is many times a - forge call's time, and short beside the check's hour. -- **A group's merges follow one another, until G's delivery.** The gate says a group's next `merge-pull` only once the previous member's - merged message is heard (ADR 0441 §5), so a member the forge refuses holds the rest, and none is merged ahead. -- **From G's delivery the gate owes no merge, and the checker nothing.** A session calls `merge-pull` itself (ADR 0666 - §11), and a group's members land one by one, the group ending when every member has (*Groups*); the checker's `owed` - goes with the checker, and the runner owes the forge nothing, a pipeline's result living on the bus. The gate still - owes the delivery's view, a comment and the note's line, performed by the forge bridge as above. -- **The bridge catches up from the seat.** At its start the bridge resumes its durable consumer and also reads - `groups`, which answers, per group, the acts the gate said with their ids and their recorded outcomes; an id said - and not answered is performed. Its tools: `bridge_owed` (the ids it performed with their outcomes, and its - consumer's backlog: the last event applied, the events waiting, the age of the oldest), `bridge_owed_again` - (perform one id again now) and `bridge_owed_drop` (drop one id with its reason, written in the hand-act log of the - seat `mesh-hand-acts`, and said as `owed.ended` with the outcome `dropped`). -- **The switch carries what is owed.** At the new gate's start (*The switch carries today's state in, once*) every key - of `owed` and `merges` the old bridge has not performed becomes an act of its record, with an id made of the key and - its revision; the new bridge counts as performed every id whose key and revision the old bridge's own bookkeeping - names, so nothing is done twice and nothing dropped. A merge handed to the forge during the switch is such an act: - its merged message finds the record holding it, so it opens no `forge-made` record. -- **A merge made at the forge has a record too.** A merge the forge reports for which no record holds a `merge-pull` - of that pull request at that head (a person merged it in the forge's own page, as on 2026-11-07 and 11-09) is taken - in like any other merge (*Taken in by the indexer it names*), and the gate opens a delivery record for it, marked - `forge-made`, with no Approve, so its trace and its outcomes on the nodes are kept like every delivery's and no - later merge waits behind a merge the gate never heard. The gate says it as the delivery's `transition` with the why - `forge-made`, which the teller passes on to the operator (the operator, 1921's note of 2026-11-07: "a merge made in - the forge is taken in like any other"). -- **How it is checked.** In the forge bridge's whole-module test: an `owed` said while the bridge is stopped is - performed once after its restart; the bridge stopped between an act and its `owed.ended` performs it again on its - restart and the forge holds it once (the comment and the note by their id); an act said twice is performed once; an - undecodable act is said `undecodable`; `bridge_owed`, `bridge_owed_again` and `bridge_owed_drop` answer, the drop - writing the hand-act log; its account cannot read the gate's or the checker's state. In the gate's: an act left - unanswered is said again at the gate's start under its id and listed by `stalled` after 15 minutes; a group's second - `merge-pull` is said only after the first member's merged message; the switch with a merge handed and not yet - merged makes no `forge-made` record; a merged message with no record's merge opens a `forge-made` record, said as - `transition` with why `forge-made`, and a later merge in the same repository is taken in behind it. Acceptance - tests 53 and 51. - ## The state table *ADR 0539 §17.* @@ -1319,475 +494,7 @@ verdict that names the trunk's head (*A move of the trunk carries every verdict* backup hold, superseded, covered, stopped, retired and failed) and the machine steps went at release 6: a module's progress on each node is its want against its have, shown by the view (ADR 0539 §17). -**From milestone G's delivery the gate keeps no merge states.** With no check before a merge and no Approve per merge -(ADR 0666 §10, §17), nothing is `proposed`, `ready`, `rejected` or `held` before a merge: a commit's states are its -pipeline's steps, kept by the runner (*Pipeline steps and the policy*). The table, the gate's `table`, `recheck` and -`close` and the one key per pull request go with the check; `stalled` lists the acts owed to the forge past their 15 -minutes and the records `record-open` names. - -## 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 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, -and one module running as several placed versions would have given one entry two shapes and one key two writers; two -graphs answer them, the second computed from the first, so there is one source of truth (ADR 0656; research 085, -Findings 11). How each is kept, read and retained is this design's choice under ADR 0656. - -``` - manifests ──► indexer (mesh-catalog) ──► catalog graph revision ──┐ - ├──► assigner (mesh-wants) ──► assignment graph revision - placements (mesh-module-assigner) ───────────────────────────────┤ │ │ - node capabilities (node-engine seat) ────────────────────────────┘ │ └─► wants, from a trunk revision only - ▼ - a change's assignment graph − the current one = its graph difference = its delivery -``` - -- **The catalog graph is the indexer's.** The indexer, holder of the seat `mesh-catalog`, keeps the modules as their - manifests declare them; it is today's module graph, and *module graph* stays its other name (ADR 0656). At each - revision it holds one entry per module: its bases, its packages, the toolchain version and recipe version of the - build module that builds it as ADR 0603 §2 has them, its provides and requires, its invokes and serves, and its - fingerprint, with the rules the seats declare (*An acting part's new version acts first*, below). -- **The assignment graph is the assigner's, computed and never written by hand.** The assigner, holder of the seats - `mesh-module-assigner` and `mesh-wants`, computes the module versions placed on nodes, with the edges between - versions, from the catalog graph, the placements and the nodes' capabilities, and nothing else writes it - (ADR 0656). Each entry is one module version on a node; its edges are provisioning, contribution, and the acting - parts' versions that are to act on it (ADR 0656), whose rules and versions come in with the catalog graph. It is - kept, but it is no stored plan: it has one writer, no part orders work by a stored plan, and any revision is computed - again from the inputs it names and compared with the one kept, by a tool (below), so a kept revision is a checked - derivation, never a second source of the kind research 063 removed (research 085, Findings 5). -- **Each graph is kept per revision, and each revision names its inputs** (ADR 0656). The indexer keeps a catalog - graph revision for each source revision it takes in; the assigner keeps an assignment graph revision for each change - of its inputs, naming the catalog graph revision, the placements and the capability readings it was computed from. - Both parts are the foundation (ADR 0619 §1), so both keep their revisions as objects declared in an `objects` - section in the old form, each with a cap it declares there, never in the new form (ADR 0619 §2), and neither waits - on the bus module to start; the manifest validator refuses either manifest that declares them in the new form - (ADR 0619 §3). An assignment graph revision is announced by an event of the seat `mesh-wants` and served by its - verbs. This replaces ADR 0603 §2's one key per module with five past values as the graph's form (ADR 0656, which - changes ADR 0603 §2); the keys are written no more, and a revert of milestone G restores them from their backup - (*Reverted*, below). -- **A revision is made by a merge, and also with no merge.** The indexer makes a catalog graph revision when it - takes a source revision in. The assigner makes an assignment graph revision on each new catalog graph revision, on - the seat `mesh-module-assigner`'s event `assignments-changed` (ADR 0567 §5, ADR 0641), and on the node-engine seat's event - `capabilities-changed`, which a node-engine says when the capabilities it states in its node record change - (ADR 0614 §4), so the assigner follows an event and never reads a node record across the seam (ADR 0596). A - placement change is traced like a merge. At its first start with no revision kept, the indexer takes in the head the - gate's source names for every repository by its catch-up path and keeps that as revision 1, and its verb `revision` - answers that it waits until then (lead-47's ruling in G's prepare: a first start has no merge to make a revision). -- **A revision is kept while a record pins it or it is current.** A revision of either graph, and the input revisions - it names, is removed when no delivery record pins it and it is not current: neither trunk's newest revision nor the - newest of an open candidate. Pins are counted per revision, so a revision two records point at stays until both - 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. -- **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. 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..` from a trunk - revision's assignment graph only; a node-engine reads its want, never the assignment graph, and the have stays what - it runs (ADR 0656, which changes ADR 0571 §8). -- **From G's delivery, per watched branch and between artifacts.** The catalog graph is kept once per watched branch, - each revision naming its branch, and its edges run between artifact references, of the six kinds of *Dependencies - between artifacts* (ADR 0666 §6, §14). The assigner computes a node's entries from the catalog graph of the branch its - placement names, and writes a want from a revision of that branch whose `required` steps passed, in place of a trunk - revision's alone (ADR 0666 §9, which changes ADR 0656 §2). -- **Reverted.** Only the new form is written: the indexer, the assigner and the gate keep nothing of ADR 0603 §2's - keys or of `merges` and `turns` beside the new forms, since nothing reads them after milestone G. A revert of - milestone G is a change like any other, checked and delivered, and the delivery system restores what it must - (*A module's kept data on the bus, backed up and restored with its delivery*, under *A backup before the apply*). - Milestone G changes the form of these kept data, and raises their `form` (a kept data declaring none reads as form - 0, so G's own move takes their restore points): - - | 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`; the states `holds`, `engine-reads` and `hold-bounds` of its mesh-wide hold | catalog graph revisions as objects, form 1; `modules` no longer declared, nor its tool `indexer_read_modules` or its view; the mesh-wide hold, its three states and its tools `indexer_holds` and `indexer_engine_reads` gone, the wait per node being the assigner's (*Composing waits per node, by the assigner*); every reader of it reading the catalog half (*Every other reader of which modules exist*, under *The revision view*, below) | yes: `modules` and the hold's three states as they 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 | - - The wants keep their form: their keys and values are unchanged, only what the assigner computes them from changes, - so G raises no `form` on them and a revert restores none. Restoring the wants would send nodes back to older - 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. 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 - -- **The difference names everything a plan named.** For a change (a pull request's heads, a delivery group's - heads, or a merge), the indexer gives its catalog graph and the assigner its assignment graph. Compared with the - current revision, the difference names the new versions, the moved edges, the touched nodes and the order, read off - the edges, and no plan is stored (ADR 0656). The assigner serves it by the seat `mesh-wants`'s verb `difference`, - for two revisions of the assignment graph or for a candidate against trunk. ADR 0603 §6's tiers, as ADR 0633 §5 - changes it, are a view worked out from that difference (ADR 0656, which changes ADR 0603 §6), read through - `difference`, with the waits and holds read through their owners' tools and the pending revisions through - `indexer_pending`. -- **The preview is a candidate's difference.** Issue 1874's plan preview of a group before its merge is its - candidate's difference, read by one call of `difference`, and needs no code of its own: a candidate's graphs are - computed for its check anyway, so the preview costs nothing more. -- **A changed fingerprint with no difference is refused by name.** When a module's fingerprint in the change - differs from the running one while the difference is empty, the assigner refuses it, naming the module, and the - check fails naming it. -- **A candidate's graphs are kept apart, and dropped with its heads.** A candidate's two graphs are computed per - check, kept apart from trunk's, and dropped on the gate's event `heads-replaced`, said when the candidate's heads - were replaced or its pull request closed. A candidate costs a computed graph per check. -- **Only a trunk revision writes wants**, whether a merge taken in or a placement or capability change made it; a - candidate's graph writes none (ADR 0656). From G's delivery a want comes from a revision of the watched branch a - placement chose whose `required` steps passed, and a candidate is a run on request's: the indexer builds it on the - runner's `run-started` and drops it on that run's `pipeline-ended`, the gate's `heads-replaced` no longer making - candidates (ADR 0666 §9, §18). -- **Each part acts on its own entries.** The builder, the build modules and the grants part each act when their own - entries in the new revision differ from what they run (ADR 0656), reading them through the seat `mesh-wants`'s verbs - and events, never its bus state (ADR 0596), and naming no other module (ADR 0656). The node-engine acts on its want - alone, as it does today: the want is the one record a node reads, and handing every node the assignment half would - have each node read every other node's placements. -- **A node asleep catches up when it wakes.** A node away holds nothing and catches up by its wants (ADR 0603 §7); - a want's new form reaches only a node-engine that says it reads it (ADR 0614 §4, ADR 0618 §2). -- **No delivery goes ahead of a change.** A change is delivered as its own difference, its acting parts' new versions - first in their own steps (ADR 0656); the one exception is the delivery before research 085's milestone (*The - bootstrap delivery, and the one milestone after it*, under *Milestones*; ADR 0644). - -### The revision view - -- **A pair, each half served by its one owner** (ADR 0656). For a trunk revision, or for a candidate's heads before - any merge, the indexer serves the catalog half by the seat `mesh-catalog`'s verb `revision`: the catalog graph, the - heads, the seat definitions, the manifests' verdicts and the fingerprints. The assigner serves the assignment half by - the seat `mesh-wants`'s verb `revision`: an assignment graph revision, named by its own revision and naming its - inputs, the catalog graph revision, the placements and the capability readings. A part's view for the current - revision is the newest assignment half and the catalog half it names. `seat-definitions` stays the seat list's verb - (ADR 0636 §4), and the catalog half carries the same definitions at its revision. -- **Every part takes a first view, then follows the events.** The checker, the builder, the grants part and the - indexer's own later passes take a first view from the two verbs and then follow the two seats' events, never a list - compiled into their build nor another module's bus state (ADR 0656, ADR 0596). The node-engine reads only the catalog - half, for the seat definitions it composes with, and its want; it never reads the assignment half (*Each part acts on - its own entries*, above). Of the view the assigner reads only the catalog half, never its own output back; its other - inputs stay the placements and the capabilities (ADR 0656). A running part acts only on a trunk revision's view; a - candidate's view is read only by the parts that judge a candidate, inside the check its manifest validator and - composer, and the indexer's candidate pass (ADR 0656, which changes ADR 0636 §4 in that the candidate pass joins - them). From G's delivery a running part acts on a revision of a watched branch as on a trunk revision, and a - candidate's view is read only by a run on request's steps and the indexer's candidate pass (ADR 0666 §6, §18). -- **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*, - 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). 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 has no view - today; milestone G adds there a view follower with its read and ask-again tools, beside the proved caller, a - verb's `callers` and a seat's publish, 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. Its cost: a release of the TypeScript side rebuilds every TypeScript module, which - the delivery carries like any change of a package its users build in. - - **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 0663, which changes ADR 0621 §1, §2 and - ADR 0615 §3; accepted); 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*). -- **The indexer reads every definition before it judges.** At its start the indexer reads the seat definitions of - every contracts module's trunk through the forge (the seat `git`'s verbs) before it judges a manifest or answers - `seat-definitions`, and only then follows the forge bridge's stream; a definition merged while it was down is - therefore never judged against a list that lacks it. The design's choice under ADR 0636 §1 and §4, as the operator - asked it (1921's note of 2026-11-07: "the indexer reads its seat definitions at start"). How it is checked: in the - indexer's whole-module test, started with a definition on a contracts trunk that no event announced, the first - manifest claiming that seat is judged with it. -- **Who holds a seat is applied by one rule, from what each node runs.** The node-engine and the grants part decide a - seat's pass by mesh-sdk's `SeatsToPassOn` (ADR 0660 §3), reading the manifest of the build each assignment runs - (ADR 0660 §2). What each node runs is heard, never asked: each part keeps it from the node-engine seat's events - `running..` (*What each step says*), through a durable consumer of its own declared in its `consumes`, - so a pass is decided with no call to a node that may be away. The seat's stream keeps the last message per subject - with no age limit, so the last `running` of every module on every node stays on it however long ago it was said, - and each part's durable consumer starts from the last message per subject, never from now: a consumer made after a - node's last `running`, and two parts' consumers made at different times, hold the same builds. The event carries the module, the fingerprint and the - digest of the build's manifest, which is the indexer's manifest of that build (ADR 0660 §2's "the indexer's - manifest of it"), read by digest from the catalog half; the builder's done key is not needed, since the event names - the digest it would give. A node that has said no `running` for a module since the part's consumer began counts by - the trunk's manifest, as ADR 0660 §2 has it where the running build cannot be read, and both parts apply that same - fallback in the one function (ADR 0660 §3). Never another node's have key, its want or the assignment half (ADR 0596 - §2; *Each part acts on its own entries*). The design's choice under ADR 0660 §2: the node that runs a build says what - it runs, and an event kept by a durable consumer answers when that node is away. How it is checked: test 56. -- **A part with no view says it waits.** It names the revision it has no view for, acts on nothing new, and keeps - running what it runs, as ADR 0636 §1 has a reader of the seat list do; the checker, the builder and the grants part - wait too, and wait for the view, not only the seat list (ADR 0656, which changes ADR 0636 §1); the node-engine waits - for the catalog half as ADR 0636 §1 already has it wait for the seat list. Each answers in its runtime's first-view - tool (below) the revision it holds, the one it waits for and since when, and after five minutes without a view writes the finding - `view-waits` naming itself and the revision, as a reader of the seat list does, which the condition keeper raises - (ADR 0564 §2); it clears when the view arrives. Five minutes is the bound the seat list's readers already keep, so an - indexer or assigner down for a restart raises nothing. A part restarted while the indexer or the assigner is down - waits and says so. -- **Each process knows its own build from its start.** The node-engine hands every process it starts the - fingerprint of the build it starts it with, and the process takes its own build from that, never learning it later - (ADR 0656, which changes ADR 0639 §7 from the indexer to every process). *The fingerprint handover*, below, says its - form. - -### What ADR 0656 leaves to this design - -ADR 0656 leaves how each part is built to this design, under issue 1921; these four needed a shape of their own. - -- **An acting rule's fields.** The seat's definition is where ADR 0656 has an acting part declare what it acts on. A - seat's definition gains one optional field `acts`, a list of rules, each with three fields of its own: `step`, one of `take-in`, - `validate`, `check`, `build`, `compose`, `grant` and `place`, and from G's delivery `pipeline`, the runner's, in - `check`'s place (ADR 0666 §19); `on`, either `every` (every module) or a `language` - naming the language of the modules it acts on, matched against what the manifest says its language is, as the - indexer's language map matches today; and `bound`, how long an entry waits in that step for the acting part's new - version (*An acting part's new version acts first*, below, gives each step's default). The definition format's own - schema (design 26) gains the field, and the format stays 1. The indexer reads it into the catalog graph, the assigner - into edges, and the checking program from the revision view. **Its cost in mesh-sdk:** the seat-definition reader in - mesh-sdk's runtime refuses a definition with a field it does not know or a format other than 1, so a reader built - before the field refuses every definition that carries `acts`, and a refused definition blocks new claims of that - seat only (ADR 0636 §3), never what runs. The runtime therefore learns `acts`, which rebuilds every module once - (ADR 0656, Consequences); that is the same rebuild the fingerprint handover makes, so the milestone rebuilds every - module once in all, and no other module's code changes. A definition carrying `acts` reaches only readers built with - that runtime: the seat definitions that gain it are members of milestone G's group, and every reader is rebuilt in - the same delivery; the format's reader itself is delivered before them, as a release of mesh-sdk's seat-definition - reader merged ahead of G's check (the operator's choice of a reader first, 2026-11-09; ADR 0667 §4). The bootstrapped - definitions every reader carries from its start (ADR 0636 §2), of which `node-backup` carries no `acts`, are made - again from the new definitions in the same change, and mesh-sdk's existing test that each bootstrapped copy equals its definition holds - them to it. -- **One wiring of the seat list's reader.** The reader of the seat list and its two tools of ADR 0636 §7 are wired once, - by mesh-sdk's runtime (`go/seatlist`), for every part that reads the list; no part wires its own. Four delivery - parts (the assigner, the checker, the indexer and the grants part) each carry a hand-wired copy today, each with its - own pair of tools, and each copy would have to learn `acts` apart. The design's choice under ADR 0636 §7, which puts - the tools in the runtime bundle: one wiring learns `acts` once, and every part's two tools are the same, named by - their module, with the same callers (any person and the healers to read, the operator and the healers to read - again), the node-engine's among them. How it is checked: mesh-sdk's seat-list test registers both tools for a - fixture part; a search of the delivery parts' code finds no wiring of its own. -- **The fingerprint handover.** The node-engine starts each process, a service or a container alike, with the - environment value `MESH_FINGERPRINT`, the fingerprint of the want it applies; mesh-sdk's runtime reads it once at the - process's start and holds it as the process's own build (ADR 0656). A process started with no value says `own build - unknown` in its runtime tool and its journal, and the indexer acts on it as ADR 0639 §8 has it. **Its cost:** one - change of mesh-sdk's runtime, which rebuilds every module once (ADR 0656, Consequences), and one of the - node-engine; the order between them is the composing step's wait of the node-engine (*Order without a person*, - below), so a module whose runtime reads the value is composed only by a node-engine that hands it. -- **The build seat's ask that runs a program in the sandbox.** The checker asks the seat `node-build-go` for an ask - `run` on the build seats' `check` stream: one whole job on one node (ADR 0525 §3), which fetches the named heads, - builds the named program at them, runs it in the run step (ADR 0448 §2) with the input the ask names by its sha256 in - the asker's own state (the checker's `inputs`, a day), asked of the seat `mesh-delivery-checker`'s verb `input` and - checked against that digest, laid as a file, and writes the program's exit and its answer, carried whole, 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 from G's delivery the runner's and the indexer's (its validation before take-in), each under its own name. - Every step key of a `run` ask starts with the asker's name (`checker.`), 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. From milestone G the seat definition of `node-build-go` in `mesh-delivery-contracts` names it; B0 - brings the ask with its types in mesh-sdk's `go/delivery` and the grants check asks have today (*The bootstrap - delivery, and the one milestone after 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. 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 - for a build node` when no build node of the language took the ask within the bound, and `did not build` when one - took it and the build failed, so the operator knows whether to free a build node or fix the change. - -### Choices this design makes under ADR 0656 - -ADR 0656 decides the direction and leaves every choice made in working it out to this design, each with its reason -(ADR 0656, Consequences; the operator, 2026-11-06 20:3x). The choices, and why: - -| 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 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 | -| No old form written beside the new; a revert restores the parts' kept data on the bus from a restore point taken before they moved, then they catch up forward | only the new form is looked after (the operator, 2026-11-06 21:3x), and a revert still works, as the delivery system's capability for every change of a kept data's form | -| A module's kept data on the bus backed up before a `form` change and restored beside the live data at a revert, a missing restore point holding the revert | ADR 0404 covers only the items on a module's node, so the bus's kept data had no restore point; a restore never goes over live data, and a revert with nothing to restore must not start on the newer form unasked | -| Only kept data whose `form` changes restored, the wants never; at a revert the node-engine last on its node, and the reverted assigner and gate only after the reverted indexer took in the trunk's head | restoring anything else would set back what was decided since; restored wants, or an assigner reading an indexer not yet caught up, would send nodes to older fingerprints; the node-engine of before may not know how to restore | -| A restore point holding the consumers' positions and the catch-up tool the newer manifest declares, called after a restore | the reverted build then hears everything since as after any restart; the stream `forge` keeps 7 days, so what fell out of it is taken in from the trunk by the part's own tool | -| 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 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 | delivering milestone G again after a revert 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 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) | -| 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` (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 | -| The acting rule's field `acts` with `step`, `on` and `bound`, the definition format staying 1 | the steps and the language match are what the indexer's map and the checker's cases held in code (research 085, Findings 2); the reader refuses an unknown field, so the runtime learns it in the one rebuild the milestone makes | -| The fingerprint handed as `MESH_FINGERPRINT` at a process's start | one value every runtime reads before anything else, a service and a container alike | -| The delivery record's consumer, held and failed outcomes, end and `record-ended`, the finding `record-open`, node away and wake, switch done once, and tools | a restart must lose and double nothing; a reader sees why an entry stands still; a node asleep must not hold a delivery open; a switch run twice must ask no approved group again | -| A small fix's own record | a small fix is a milestone of one issue (ADR 0493 §3) | -| An Approve given held while its heads stand (issue 1972) | the operator approved those heads; a check of them asked again changes nothing they approved (ADR 0441 §1, ADR 0548 §8) | -| The milestone events on a subject of their own | a waiter on one member's pull request otherwise wakes on every member's | -| The issue tracker's link kinds to a decision and a research effort, and the knowledge base's link from a decision to its milestone | the trace from research to the running build is then links all the way, with no text match (research 085, Findings 7; ADR 0493 §4) | -| The view's state read and asked again by mesh-sdk's `go/views` tools `_first_views` and `_first_view_again`, which gain the revision a part holds and what it waits for; the typed first view and the following of each half in the seats' client packages `meshcatalogclient` and `meshwantsclient`, built on `go/views`; the finding `view-waits` after five minutes; and `node_process_builds` | every part's view and every process's own build is read and steered by a tool; `go/views` already follows an owner's events durably and serves those two tools, so a second follower or a second pair of tools would be two forms of one thing; a view tool typed by the two seats would make mesh-sdk import contracts (ADR 0635 §4), so the typed half sits in the seats' client packages (ADR 0625 §4); a part waiting for the view is raised on the bound the seat list's readers keep | -| The seat list's reader and its two tools wired once by mesh-sdk's runtime | ADR 0636 §7 puts them in the runtime bundle; four hand-wired copies would each learn `acts` apart | -| The trunk's heads from the catalog half, and nothing read of the gate by the checker | ADR 0596 §2; with the hold and turns removed nothing of the gate is left for the checker to read | -| The indexer reads every contracts trunk's definitions at its start, before it judges | a definition merged while it was down is never judged against a list that lacks it (ADR 0636 §1) | -| Another node's running build kept from the node-engine seat's `running` events by a durable consumer, the event carrying the fingerprint and the manifest's digest, the trunk's manifest for a node that said nothing | ADR 0660 §2 reads the manifest of the build each node runs; an event kept by a durable consumer answers while a node is away, where a call would wait on it; one fallback in the one function keeps the two parts agreed (ADR 0660 §3) | -| What the gate and the checker owe the forge said as the event `owed` per repository and pull request, each act safe to repeat at the forge, each outcome said as `owed.ended` and recorded, an unanswered act said again at the gate's start and stalled after 15 minutes | ADR 0596 §1, §2 end the watches of the gate's and the checker's keys; an act safe to repeat cannot be lost between the act and its record, where an id recorded first could; an outcome said and recorded leaves no act unseen | -| A merge made at the forge opens a `forge-made` delivery record | its trace and outcomes are kept like every delivery's, and no later merge waits behind a merge the gate never heard | -| Every delivery part and the gate reach the forge only through the seat `git`'s verbs and events, the act verbs granted by the holder's trusted `callers` to the forge bridge alone, a failed read answered as an error | the delivery parts name no other module (ADR 0656 §1, acceptance test 27); the forge's answers are typed in the seat's contract instead of guessed in each caller; the bridge stays the one part that acts on the forge; an error read as an empty answer would hide a forge down | -| A check that failed because a part of the mesh was not heard says `could not run`, naming the part, releases its claim, and is asked again once per heads and part on that part's `running` event, raising `check-could-not-run` after 60 minutes | the operator sees a mesh fault apart from a failing change, and the check is asked again on the one event that can change its outcome, never on a timer (issue 1373) | -| A module held for its claim raises `module-held` until it is composed | a module left out in silence would look delivered (the mesh's rule that failure is loud) | -| The indexer waits for the seat `git` at most 2 minutes at its start, then judges from its kept definitions and raises `view-waits` after five | longer than a forge's restart, and no start held behind a forge down for good | -| The forge before the forge bridge before the gate, reverted the other way | the verbs are served before they are called, and `owed` is said only to a bridge that hears it | -| The store seat's holder's own new build kept on its build node and fetched by `kept-artifact` when its seat is broken | its own build needs the store it replaces; a step of the delivery replaces the hand bootstrap | -| Every drop and release tool writes the hand-act log | a hand act that changes what the mesh runs or keeps is recorded with who did it and why, as every hand act is (seat `mesh-hand-acts`) | - -Each choice is checked by the tests its section names; the review by architectural domain judges them. - -### What each step says - -Each step says what it did as its own event, the event of the part that did it. A catalog graph revision kept -carries the source revision; an assignment graph revision kept and a difference worked out carry the source revision -and the assignment graph revisions; an entry taken in, built, checked, composed, wanted and running also carries the -entry's fingerprint and its node. The gate's delivery record is built from them. The seat's event comes first where -the parts follow it through a seat (ADR 0656); a holder's own event stays under its own subject, declared in its own -manifest's `streams` and published on `mesh.stream..event.` (ADR 0363 §1, §2). A seat's event is -declared in its definition and its holder's manifest claims the seat; a seat event's name has no dot, since it is read -as a subject's token, and it is kept on the stream `SEAT__`, which the grants part makes with its durable -consumers, keeping it by the age and cap the definition's `keep` names, 90 days unless it names less. **An event a node publishes carries the node in -its subject**, and the grants part grants each node-engine's account only its own node's subjects, so one node cannot -say another runs a build (ADR 0363 §2); the gate's events are granted to the gate's account alone. - -| Step | Event | Owner: the seat, then the module | Subject | Declared in | -|---|---|---|---|---| -| a catalog graph revision kept, saying whether it is a trunk revision or a candidate's and its watched branch | `catalog-revision` | `mesh-catalog`, the indexer | `mesh.seat.mesh-catalog.event.catalog-revision`, the watched branch in the subject from G's delivery | the seat's definition in `mesh-delivery-contracts` | -| an assignment graph revision kept | `assignment-revision` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.assignment-revision` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | -| a difference worked out | `difference` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.difference` | the same | -| an entry taken in | `take-in.taken`, which gains the entries | the indexer's own | `mesh.stream.mesh-delivery-indexer.event.take-in.taken` | the indexer's manifest | -| an entry built | `build.ended` | the builder's own | `mesh.stream.mesh-delivery-builder.event.build.ended` | the builder's manifest | -| an entry checked | `checked` | `mesh-delivery-checker`, the checker | `mesh.seat.mesh-delivery-checker.event.checked` | the seat's definition in `mesh-delivery-contracts` | -| an entry wanted | `wanted` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.wanted` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | -| an entry composed, and running healthy | `composed`, `running` | `node-engine`, the node-engine on each node | `mesh.seat.node-engine.event.composed.`, `.running..`, the latter on a stream kept last per subject with no age limit | 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.` | the same | -| 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..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..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.` | the seat's definition in `mesh-delivery-contracts` | -| a milestone's ask asked, withdrawn and answered, until G's delivery | `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.`, the milestone in the event, on the seat's family stream, apart from each delivery's `transition` | the same | -| a delivery record opened, and a merge named a recovery | `record-opened`, `recovery-merged` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.` | the same | -| a pipeline, its steps, its waits and a run on request, from G's delivery | `pipeline-started`, `pipeline-ended`, `group-ended`, `run-started`, `step-started`, `step-ended`, `wait-held`, `wait-ended` | `mesh-pipeline-runner`, the runner | `mesh.seat.mesh-pipeline-runner.event....` | the seat's definition in `mesh-delivery-contracts` | -| a pipeline step ended, from G's delivery | `step-ended` | each build seat, `mesh-wants` | `mesh.seat..event.step-ended.`, the node that ran it | each seat's definition | -| a registration changed; a package published, from G's delivery | `registration-changed`; `package-published` | `mesh-catalog`, the indexer; `mesh-builds`, the builder | `mesh.seat.mesh-catalog.event.registration-changed`; `mesh.seat.mesh-builds.event.package-published` | the seats' definitions in `mesh-delivery-contracts` | - -The events, their fields, their subjects and which are a seat's are this design's choices: a seat's event where -other parts follow the step through the seat, a holder's own event where only the delivery record hears it, so that -nothing reads another module's bus state (ADR 0596). The builder's waits go on its own stream because the builder -holds no seat of its own; the watchman and the gate follow them there. How it is checked: each seat definition's -conformance fixtures name the subject of every event it gains, and a case of the holder's whole-module test matches an -event it publishes against them, as for every seat (design 26); the gate's whole-module test hears each row through -its durable consumer, with exactly the fields this section gives it; a `running` event published by one node's account -on another node's subject is refused by the bus. - -### Their tools - -| 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), `assigner_puts` (each put with its step, since when, held or not, the `running` that ended it, and for a put without hold whether it is past its 30-minute bound; a put older than the trunk's newest build of its module said so) | `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` | - -Every drop and release tool writes its act, who did it and why, in the hand-act log of the seat `mesh-hand-acts`. -Beside ADR 0636 §7's two tools in mesh-sdk's runtime bundle, which read a reader's seat list and make it read the -list again, the view is read and steered by the two tools mesh-sdk's `go/views` already serves every part that takes a -first view: `_first_views` gains which view revision the part holds, what it waits for and since when, beside -what it heard and how far it is behind, and `_first_view_again`, granted as ADR 0636 §7's second is, makes it -read the view again now. `go/views` stays seat-neutral, naming no seat and no seat's type (ADR 0635 §4); the typed -first view of each half and the following of its events are the seats' client packages, `meshcatalogclient` for -`mesh-catalog` and `meshwantsclient` for `mesh-wants`, each built on `go/views` (ADR 0625 §4), so no part follows the -view by a follower of its own. A tool -of the node-engine, `node_process_builds`, reads the build each process was started with. The tools answer the four -lines every module's tools answer (design 26), so that every state these parts keep is read and every act they do is -steered by a tool. - -**How it is checked.** Unit (assigner): a revision of the assignment graph computed again from its named inputs -equals the one kept; a fixture change with a new fingerprint and an empty difference is refused, naming its module; -one revision of about 120 modules on four nodes stays under the bus's value limit; of the view the assigner reads only -the catalog half. Unit (indexer): a catalog graph revision holds one entry per module of its source revision. Whole -(indexer): a source revision taken in gives one catalog graph revision kept as objects in an `objects` section in the -old form within the cap its manifest declares; the manifest validator refuses the indexer's or the assigner's manifest -that declares them in the new form. Whole (indexer, assigner): a revision a delivery record pins is not removed; one -two records pin stays until both free it; one neither pinned nor current is removed; a `freed` said while the part -was stopped is applied after its restart; a candidate's graphs are dropped on `heads-replaced`; a store past 80 % says -`filling` and raises `store-filling`, and a full one says `full` and refuses its next revision naming itself; for a -candidate's heads both `revision` verbs answer, the assigner's half named by its own revision and naming its inputs; a -placement change with no merge, and a `capabilities-changed`, each give a new assignment half on the same catalog half, -whose difference moves only the module concerned, and a part takes that pair as its view; after its first view a part -acts on a seat event without asking a verb again; a running part does not act on a candidate's view; a pull request -that adds a seat and a module using it is validated by the indexer's candidate pass and checked by the checker with -the same definitions; the tools of the table answer and the drop tools write the hand-act log. Whole (assigner): a -pull request's graph leaves every node's want unchanged; a placement-only revision moves the want of the module it -places; the builder and the grants part read their entries only through the seat `mesh-wants`'s verbs and events, and -the node-engine reads its want and the catalog half and never the assignment half. Whole (node-engine): a part started -while its node's want differs from what runs reports the build it was started with. Live: a search of the readers' -code finds none of the reads the revision view replaces, each by name: the indexer's read of the checker's previews, -the checker's watch of the indexer's `candidates.`, the forge bridge's read of the gate's `merges` and `owed`, -the checker's read of the gate's `source` and `turns`, the forge module's watch of the checker's `owed` (its `mesh/check` -status, now the checker's `owed` event performed by the forge bridge) and its read of the gate's `source`, the grants -part's read of the assigner's state `wants`, any module's read of the indexer's `modules` (its tool -`indexer_read_modules` in an `invokes`, its view in a `consumes`), the -checker's validator request with no seat definitions, a seat list compiled into the node-engine, the checker, the -builder or the grants part, a seat list's reader wired by a part itself, and a call of the forge module's own tools by -a delivery part or the gate; an old node-engine that wakes takes its new build in a form it reads. +**From milestone G's delivery** the gate keeps no merge states: design 47 G, *The state table, from milestone G's delivery*. ## Order without a person @@ -1819,135 +526,6 @@ per module and node from the keys: that holds one is told once on its pull request that the line is not read (ADR 0419 §2), until milestone G, in which that notice goes with the gate's last read of a description (ADR 0659 §4; *The Approve and the merge*). -### An acting part's new version acts first, a wait with a bound - -*ADR 0656, which supersedes ADR 0646; ADR 0603 §1 to §4, §8, §11, ADR 0614 §1, §3, §6, ADR 0633 -§1, ADR 0539 §7, ADR 0584 §1, §2, ADR 0564 §2.* - -From research 085's milestone. A seat's definition may carry a rule, the step its holder acts in and the modules it -acts on, and a holder of such a seat is an **acting part** (ADR 0656): the indexer taking in and validating, the -checker checking, the build modules building, the node-engine composing, the grants part granting, the assigner -placing. *What ADR 0656 leaves to this design*, above, gives the rule's fields. Adding one needs its manifest and -its seat's rule, and no code elsewhere: the indexer's map from language to build module and the checker's own module -names go, since every new acting part needed code changes elsewhere (research 085, Findings 2). - -- **The rule becomes edges between versions.** In the assignment graph the assigner gives an edge from the acting - 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. 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 - its language (ADR 0584 §1) names the new build module, never merely until the new build module runs on one node: a - fingerprint names the agreed toolchain (ADR 0603 §3), so only then is the module built once, by the new one. A build - 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..` 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 - 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 - 4; stopping the whole delivery would hold modules the failed part does not act on (the operator, 2026-11-06 17:0x). -- **A wait holds only within its step, and skips the holder's own inputs** (ADR 0656). An entry waits only for its - own step's holders, so two acting parts that act on each other in different steps (the indexer taking in the - checker, the checker checking the indexer) form no cycle. Within a step a holder's own change is its first act, and - what its new version is built from or needs (a base, a package, a provider) is acted on by its running version, - never waited for: the running build module builds the shared contracts and the registry holder, then the new build - module builds the rest, so ADR 0614 §6's cycle through them stays allowed, now for that reason. -- **The artifact store's holder takes its own new build even when its seat is the one broken.** A build module - stores the artifacts it builds through the seat `oci-artifact-registry`, so the - holder of that seat is in its own build's path: its new build needs the store it replaces. As every holder's own - inputs are (above), that store is acted on by the running holder: the running holder stores its new build, and only - then is the new one wanted. When the running holder is what is broken (the seat refuses or does not answer a store, - as in the hand bootstrap of 2026-11-07 to 11-09), the build module keeps that one build on its own node among its - kept builds (*The bootstrap delivery*: `build_go_kept`), says in its step key that it is kept on that node, and the - builder's done key names it so with its digest; the node-engine composing the holder fetches it through the build - seat's verb `kept-artifact` of that node, and the new holder, once it runs, takes the store of every build after it. - - The verb sits in the build seats' definitions (`node-build-go`, `node-build-node`, `node-build-image`, in - `mesh-delivery-contracts`), and its callers are the node-engines alone: the definition names them, and the grants - part grants it to no other account. - - It answers only for a module that claims the seat `oci-artifact-registry`, and refuses every other, naming the - module, so a store outage never turns into delivering from build nodes. - - The bytes go as an object by digest on the bus (the old `objects` form, ADR 0619 §2), never in a value, since an - artifact is over the bus's value limit; the node-engine checks the bytes it fetched against the digest in the - builder's done key and applies nothing that differs. - - While the keeping node is away, or its sweep removed the build, the want waits, and the node-engine says in its - have that it waits for that node or for a build no longer kept. - - This is the one artifact the mesh delivers from a build node instead of the store, and only for the holder of - `oci-artifact-registry`; the design's choice under ADR 0656 §3 (a wait skips what the holder's own new version needs) and ADR - 0526 §2, whose build by a session's call it makes a step of the delivery instead of a hand act. How it is checked: - test 60. -- **The check refuses a cycle among the waits within one step** (ADR 0656, which changes ADR 0603 §2's refusal of a - cycle), counted beside the edges ADR 0614 §3 and §6 list; a cycle through an acting part's edges across two steps, or - through a holder's own inputs, is none. -- **The indexer's own steps have no wait, and a newer indexer is reached.** Taking in and validating stay as *Taken in - 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 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. -- **An acting rule grants nothing.** A holder's bus grants come from the grants part as today (ADR 0539 §7). An - acting rule changes what a part acts on across every module it matches, so the operator's Approve ask of a change - names any acting rule it adds, and any it widens: a `step`, an `on` or a longer `bound` that now matches more - modules or holds them longer is named like an added rule. From G's delivery there is no Approve ask per merge, and - the pipeline's end names it in `widens` instead, refusing nothing (ADR 0666 §23). -- **Each step's wait is its own part's, read and released by its own tools.** The part that does a step holds its - wait, so each wait has one owner that already holds that step's state, and says `wait-held` and `wait-ended` (*What - each step says*) with the step, the acting part, its version, the entry and since when: - - | Step | Holder | Reads its waits | Releases one | - |---|---|---|---| - | 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 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. -- **How it is checked.** Whole (assigner): a fixture seat declaring a rule, with a fixture holder, gets its edges with - no code change elsewhere. Whole (assigner, builder, three build nodes): a group changing the shared contracts and the - build module that builds them, the new build module updated on the first build node first, builds no module with the - 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, - 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 (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 - through a build module's edge each pass the cycle check, each new version first in its own step; a cycle among - waits within one step is refused. Unit (assigner): the Approve ask of a change that adds an acting rule, and of one - that widens one, names the rule. Live: a search of the delivery parts' code finds no other module's name. - The planner's edges (*built by*, *SDK before its users*, *verb before caller*, *version skew*) and its one default, the node-engine before the controller, ordered the tiers of a plan until release 6 (ADR 0419 §3 to §5). For release 6's own switch, the last plan of release 5's controller, the version-skew edge and that @@ -2063,18 +641,7 @@ the check gates merges.* *ADR 0548 §2 to §4, §9, §10, as ADR 0551 bounds them; from research 085's milestone removed by ADR 0661 (accepted on the operator's Approve; below).* -**Removed at research 085's milestone.** The hold, the merge turns, their stalled line, the gate's tools for them and -the setting `check-gates-merge` go in milestone G, the gate's and the forge module's alike (the operator, on Telegram, -2026-11-09 01:11 UTC, choosing "Remove them in G"; the forge's merge then judges no check, *The seat `git`*). The setting has been false since 2026-10-25, so the gate wrote no turn and held no merge in that time, and the -operator's word for G is that no check gates delivery (1921's note of 2026-11-07). From G's delivery no merge waits on a check, and none on an Approve either (ADR 0666 §10): the checker is gone, and the -pipeline runs on the landed commit (*Pipelines per module*). **Its record is accepted:** ADR 0661 ends ADR 0548 §2 -to §4, §9 and §10 and ADR 0551 and changes ADR 0534 §4's judgement of `mesh/check` by the merge; until G's delivery the -rule below holds as those records have it. ADR 0662 is withdrawn; G builds ADR 0661 §1, §2 and §6, and ADR 0666 §10 -changes its §3 to §5 (the merge on the Approve alone), which G therefore builds for no merge. Beside it, ADR 0663 (accepted: the indexer answers `revision` in its own runtime to -exactly the parts that invoke it, the tool runner among them) holds the tool -runner's first view of the catalog half (*Every other reader of which modules exist*). How it is checked: in the gate's whole-module test an approved merge into a -repository under a running check is handed at once, and the gate's manifest declares no `turns` and no setting -`check-gates-merge`; in the checker's, a check claims at once with no gate state granted to read. +**Removed at milestone G:** design 47 G, *Nothing merges into a repository under check: removed*. Until milestone G's delivery: @@ -2172,80 +739,6 @@ From its delivery, a check is this: heads in the check's first tier and composes with its own code (ADR 0656; *The check on the change's own acting parts*, below). -### The check on the change's own acting parts - -*ADR 0656, which supersedes ADR 0648, ADR 0644; ADR 0448 §2, §4, ADR 0391 §1, ADR 0603 §10, ADR 0614 §7, ADR 0633 -§6, ADR 0636 §4, ADR 0639 §5, ADR 0498 §1.* - -From research 085's milestone. ADR 0603 §10 and ADR 0614 §7 let a check pass while the part that would build or -check the change never ran: 7 of 24 failures since 2026-11-01 (research 085, Findings 4). The acting parts a change -alters, and the checking program, the rules every check applies, are built from the change's heads and check it; the -running versions never check in their place, and a check fails, naming the part, when one cannot be built or run -(ADR 0656). - -- **Two tiers on the change's assignment graph.** The first tier builds, at the change's heads, only the acting parts - whose fingerprint in the change's assignment graph differs from the running one and which are to act on a module - the check acts on; a change to an ordinary module asks no first-tier build, since building every acting part for - every check would make an ordinary change pay for builds that change nothing. The second tier runs the check's - levels with those programs and with the revision view of the change's heads (ADR 0656). This keeps ADR 0603 §10's - building of changed build modules first and widens it to every acting part. -- **The judging rules are a checking program built from the change.** Every check builds its checking program from - the change's heads and runs it with the revision view as its input; when it cannot be built or run, the check - fails naming it, and the running checker's own rules never judge in its place (ADR 0656). It is built once per - fingerprint and reused (ADR 0498 §1): checks whose heads leave the checking program unchanged ask one build between - them. -- **Every program built from a change runs only in the build seat's sandbox.** The acting parts the first tier builds - and the checking program run in the run step, with no network (ADR 0448 §2) and no bus account, the logins staying - outside (ADR 0448 §4), through the seat `node-build-go`'s ask `run` (*What ADR 0656 leaves to this design*), as - the validator, the composer and the Go build module already run; a change not yet merged is not trusted. The running - checker alone asks, reads the program's verdict, records it and publishes it. -- **A first tier that cannot build an acting part fails the check, naming it.** The verdict never passes on the - running version's word: a module whose changed build module the check cannot run is no longer composed from its - manifest (ADR 0656, which changes ADR 0603 §10 and ADR 0614 §7), and ADR 0614 §7's reason, a changed build module - 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 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. -- **ADR 0633 §6 stays, beside the change's own validator.** Whether the running parts can take the change in is - still checked with the trunk's manifest validator, built from the trunk (ADR 0633 §6); the change's own validator, - built in the first tier, judges the change's manifests besides (ADR 0656). A change fails if either refuses it. -- **The asking and recording are covered by the checker's whole-module test**, which starts the changed checker on a - real bus and asks one check of a fixture group end to end (ADR 0391 §1). -- **One seat list for both readers of a change's heads.** Inside every check, a lone pull request's as a group of one, - the checking program built from the heads makes one seat list from them, and the checker hands it to the manifest - validator and the composer (ADR 0644, ADR 0636 §4 with its insight of 2026-11-06); the running checker reads no - definition of the heads itself (*The bootstrap delivery, and the one milestone after it*). -- **Its tools.** The checker's read of a check names the checking program's build that judged it, its fingerprint and - the heads it was built from, and the rules named changed; `checker_first_tier` lists a check's first-tier builds, - each with its acting part, its fingerprint and its outcome (`built`, `waiting for a build node`, `did not build`), - and asks one of them again. Asking a whole check again stays the checker's existing ask-again. -- **What it costs.** A check of a change to an acting part costs one build per changed acting part, within the - checker's step bound (ADR 0639 §5); an ordinary change costs nothing more; a checking program costs one build per - fingerprint, shared by every check that uses it. -- **How it is checked.** In the checker's whole-module integration test: a fixture change whose acting part does not - build, and one whose checking program does not run, each fail their check, naming the part; a checking program that - 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 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 - same checking program ask one build of it; a group that changes the assigner's placing is checked with its own - assigner; a fixture indexer change whose manifest the trunk's validator refuses fails its check, even when its own - validator passes it; a fixture group whose heads add a seat has that seat in the list both the validator and the - composer receive; the check's read names the checking program's build, heads and changed rules; `checker_first_tier` - lists a check's first-tier builds and asks one again; the changed checker asks one check of a fixture group end to - end. Live: the registry group passes its one check with its own checker, validator and composer. - ### A try: a delivery group's check at exact commits *ADR 0637, ADR 0638; ADR 0593 §7, §9, ADR 0636 §4, ADR 0548 §5, ADR 0596 §1 to §3, ADR 0615, ADR 0639 §1 to @@ -2410,13 +903,8 @@ session merges a pull request*). assigner writes each want when its six rules hold. A merge is never refused, held or dropped once it is made. What of it is taken in waits only for the indexer its source revision names, or for the rest of its group (*Taken in by the indexer it names*, below; ADR 0633). -- **From milestone G's delivery, per watched branch.** The gate writes one source revision per watched branch, - `source.`, as a commit or a group lands there; the pipeline runs on each revision the indexer takes in, and - the wants follow a revision whose `required` steps passed, on the branch each placement chose (ADR 0666 §6, §9). -- **From research 085's milestone, the wants follow the trunk's assignment graph.** A merge taken in makes a - catalog graph revision and then an assignment graph revision; the assigner writes each want from that revision, - its rules unchanged (ADR 0656), and each acting part's new version acts first in its own step (ADR 0656; *Two - graphs, one source of truth*, above). +- **From milestone G** the source revision is kept per watched branch and the wants follow the assignment graph: + design 47 G, *After the merge, from milestone G*. - **Two merges close together are two revisions.** The second's fingerprints replace the first's in the indexer's modules; a node gets the newer fingerprint's want once its rules hold, and a fingerprint nobody wants any more is never applied. Nothing waits for a window to close. @@ -2524,12 +1012,7 @@ timer. A group's merges arrive together. `take-in.taken`: a hand take-in as any take-in does, and a hand drop naming the drop, for a merge whose wait was said pending, so the watchman's `take-in-waits` clears; a drop of a merge never said pending emits none (ADR 0633 §5, ADR 0639 §9). -- **From milestone G's delivery** (ADR 0666 §6, §13, which change ADR 0633 §1 and §3 to §6): before any take-in, on every - watched branch, the running indexer validates the revision with the manifest validator built from its heads, in the - sandbox, and a revision it refuses stays pending, as the bullets above have it, with the way back a revert; a pending - revision holds only its own watched branch; a group's member reaches it by landing, and the group ends when the gate's - group verb says every member has; `bridge_group_end_again` reads that verb; and the check's refusal below goes with the - check (*Watched branches and the catalog graph per branch*, *Groups*). +- **From milestone G's delivery:** design 47 G, *Taken in by the indexer it names, from milestone G's delivery*. - **The check refuses an indexer the running parts could not move, until G's delivery.** For a change to the indexer's own module, the check validates the new indexer's manifest, found as the manifest that claims the seat `mesh-catalog`, with the trunk's manifest validator in its sandbox and the seat definitions of the members' heads, and refuses the change @@ -2570,99 +1053,6 @@ backup holder backs up nothing, and the have says so. - **What it costs.** About 85% of data modules back up in under 30 s; the slowest measured, postgres on the home server, about 165 s (research 055, finding 5). A module that keeps no backed-up item costs nothing more. -### A module's kept data on the bus, backed up and restored with its delivery - -*ADR 0404, ADR 0235, ADR 0233, ADR 0539 §16, ADR 0595, ADR 0619 §2, ADR 0633 §1, §5, ADR 0363 §2; from research -085's milestone, this design's choice under ADR 0656.* - -**ADR 0404's backup does not cover a module's kept data on the bus.** It backs up the items a moved module keeps on -its own node (ADR 0404 §2, ADR 0233), and the bus's streams are dumped whole by the bus module on its own rhythm -(ADR 0235), not per module and not before a delivery. A key-value state, a stream or an object store a module keeps on -the bus (ADR 0595, ADR 0619 §2), and its durable consumers' positions on the streams it reads, therefore have no -restore point taken before its module moves. That is a gap of the delivery system, and milestone G builds it, because -a revert of milestone G is the first change that needs it. It is built as the delivery system's capability for every -later change of a kept data's form, not as a case of this milestone. - -- **Its form is declared.** Each kept data a manifest declares names a `form`, a number its holder raises whenever it - changes how it writes that data, or stops declaring it; a declaration naming none reads as form 0. The declaration - alone cannot tell a changed layout of the same bucket, so the holder says it. Only kept data whose `form` changes is - backed up and restored, so a part's other kept data, and every decision it holds, is never set back. -- **It is backed up before the module moves.** When a want moves a module whose new manifest declares a kept data at - another `form` than the running one, or no longer declares it, the node agent applying it first asks the seat - `node-backup` of the node holding `mesh-broker` for a restore point of that kept data, and waits for its event - `ended`, as for any backup before the apply. The bus module's account takes it, as it takes the bus's snapshots - (ADR 0235 §5). A restore point is keyed by the module, the kept data, its form and the fingerprint it was taken - before, and it holds the kept data whole, the position of each durable consumer the module reads with, and the - catch-up tool the newer manifest declares beside that kept data. A failed - backup holds that module as any failed backup does. -- **A revert restores it before the reverted build starts.** When a want takes a module back across a change of a - kept data's `form`, the node agent applying it asks that seat's verb `restore` for the restore point of that module, - kept data and form, waits for the seat's event `restored`, and only then starts the reverted build. The node agent - that restores is the one running when the revert starts. As every restore is, it is brought back beside the live data - and never over it: the live kept data of the newer form is set aside under a name of its own, and only the operator - removes it, so nothing written since is destroyed. The consumer's position is set back with it, so the reverted - build hears everything since as after any restart. -- **The order of a delivery that crosses a form change** is the module graph's (*Order without a person*), with three - edges the assigner, which writes every want, keeps: - - the new versions of the `node-backup` holder and of the bus module run before any module whose kept data's form - changes is moved, since the restore point needs both; - - at a revert, a node's node-engine goes back last on that node, after every module it restores there has said - `restored` and runs, since the node-engine of before may not know how to restore; - - the reverted assigner and gate are wanted only once the reverted indexer has taken in the trunk's head, as its - `take-in.taken` naming the current source revision says (ADR 0633 §1), since the assigner of before computes the - wants from the indexer's `modules` and would otherwise write the fingerprints of the restore point. Until then the - running assigner writes no want, and every module keeps the fingerprint it runs. -- **The restored part catches up forward, with the code it already has.** Read on main, mesh-delivery at 2d201f78 - and mesh-catalog at 1d07778c: - - **The indexer** (mesh-delivery, `modules/mesh-delivery-indexer`): its durable consumer `_` on the stream - `forge` redelivers every message since the restored position, and `handle` takes each - merge through `Indexer.Arrive` into `pending` and then into `modules` (`applyMerged`); `applySource` takes the - gate's `source.main` in, and `ApplySource` leaves a revision older than the latest it has seen. **The stream - `forge` keeps 7 days**: the forge bridge's manifest declares no age, and a stream's default is 7 days, at most 30 - (mesh-sdk `go/catalogue` `StreamDefaultDays`, `StreamMostDays`), so it does not cover the records' 90 days. A - merge older than the stream's first message is never redelivered, and the code then leaves that repository's - modules at the restore point's fingerprints. So the restore runs one forward step: once the reverted indexer - runs, the node agent that restored it calls its tool `indexer_take_in` with each repository the gate's source names - (`NewTakeInRepository`, which takes in the head the source names for a repository). The catch-up tool is declared - by the newer build's manifest, beside the kept data whose form it changes, and recorded in the restore point when - the backup is taken; the node agent takes it from the restore point, never from the manifest being applied, since - at a revert that is the manifest from before the change, which names none. So the delivery system names no - module's tool. - - **The assigner** (mesh-delivery, `modules/mesh-delivery-assigner`): it keeps nothing restored. Its pass - (`passer.wants`) writes, from what the world holds now, each node's want by the six rules from the indexer's - newest fingerprint at the trunk, and only where the value differs; so once the indexer has caught up it writes - the trunk's wants, and nothing older. - - **The gate** (mesh-catalog, `modules/mesh-delivery-gate`): `Holder.Load` reads the restored deliveries and groups - back; `readOnStart` then reads what happened while it was away: `wantForge` reads the forge's open pull requests - at the next look, `hearAsks` reads the teller's answer to every ask it still holds as pending, and - `SourceCatchUp` brings the newest merge of each repository into `source.main`, never moving a repository back. An - ask the gate of milestone G raised after the restore point is not among the pending ones, so its answer is not read - and the operator is asked again. Whether the first look clears a merge kept in the restored `merges` that the forge - merged since was not read in the code; the catch-up test below proves it with the real builds. -- **A missing restore point holds the revert, naming it.** When the restore point a revert needs does not exist, or - `restored` does not come, the node agent applies nothing of that module, keeps what runs, and writes the have phase - `held` with the reason `restore point missing` and the module, kept data and form it names; the assigner asks the - operator (hold, or start the reverted build on the live data as it is), raises the condition naming them, and on - the answer writes the node's want again naming it (ADR 0539 §16). -- **What the operator is told.** A revert across a change of a kept data's form puts that kept data back to the - moment before the change moved it: the deliveries made since stay delivered, but what the part wrote there since - (for milestone G, its delivery records and its graph revisions) is set aside and not read again; an Approve asked - and answered since is asked again, and one the gate asked before still reads its answer. The revert's Approve ask - says this in those words, naming each kept data restored. -- **Its tools.** The seat `node-backup`'s `backed-up` lists each module's restore points of its bus data, with the - kept data, the form and the fingerprint each was taken before, and the data set aside at each restore, with its - size; `restore` restores one; `discard-aside` removes data set aside by a restore, only on the operator's call, and - writes the act, who and why in the hand-act log of the seat `mesh-hand-acts`. `backed-up` and `restore` exist on - the seat today and gain the module's kept data on the bus as an item; `discard-aside` is new. -- **How it is checked.** Whole (node-engine, backup holder, bus module): a want moving a module across a `form` - change, and one moving a module whose kept data declares no `form` to one that does, each take a restore point - first, keyed by module, kept data, form and fingerprint; a want taking it back restores it beside the live data and - the consumer's position before the reverted build starts, and the data set aside is listed with its size and - removed only by `discard-aside`, which writes the hand-act log; a revert of a group holding the node-engine and a - module whose form changes restores the module before the node-engine goes back; a revert whose restore point is - missing is held, naming it, and moves on only on the operator's answer. The catch-up is proved with the real builds - of the indexer, the assigner and the gate from before milestone G, never fixtures (acceptance test 36). - ## Compositions, wants and the apply *ADR 0360 §8, §9, ADR 0377, ADR 0429 §4, §6, ADR 0539 §9, §14 to §16, ADR 0546 §5, §6, ADR 0547 §2 to §9, ADR 0549, ADR 0555.* @@ -3039,153 +1429,9 @@ forge module's files of what it announced (ADR 0406 §6). `times`, `what-if`, `release`, `stop` and `retire-history` went at release 6; each is answered that it no longer exists. -From research 085's milestone the seat gains: - -| Verb or event | Answers or does | -|---|---| -| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656), and the acts the gate said it owes the forge for it, each with its id and its recorded outcome (*What the gate owes the forge*) | -| `owed` | an act the gate owes the forge (a merge, a status, the delivery's view, a comment, a line on the commit's note), with its pull request, head and id, on `mesh.seat.mesh-delivery.event.owed..`, said only from the gate's account and heard by the forge bridge through a durable consumer (*What the gate owes the forge*) | -| `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | until G's delivery: the one Approve ask per milestone (ADR 0493 §9 as ADR 0659 §1 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.`, the milestone in the 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`. G's delivery ends the ask and its three events with the Approve per merge (ADR 0666 §10) | -| `record-opened` | a delivery record opened, which the watchman times for `record-open` | -| `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`, `room` | a delivery record ended; the ledger past 80 % of its cap; the ledger full; the ledger back below its cap (*The delivery record*) | - -From milestone G's delivery the seat's `table`, `recheck` and `close` go with the merge states (*The state table*), -the milestone ask and its three events with the Approve per merge, and it gains (ADR 0666 §6, §26): - -| Verb or event | Answers or does | -|---|---| -| `source` with a watched branch | that branch's source revision; the indexer and the node-engine's catch-up read it, never the gate's state | -| `source-moved` | a new source revision of a watched branch | -| `recovery` | marks a merge its author names a recovery (the commit and why), its record marked `recovery` with the caller the runtime proves | -| `record-opened`, `recovery-merged` | a delivery record opened; a merge named a recovery, which the teller passes on to every channel | - -**The seat `mesh-pipeline-runner`**, from milestone G's delivery, defined in `mesh-delivery-contracts` with the callers -of each verb (ADR 0666 §7, §20, §25): - -| Verb | Answers or does | Callers | -|---|---|---| -| `run` | the run on request of one or more members, each a module at a named commit; delivers nothing | the sessions (the agents' bus identities the tool runner proves) and the operator, nothing else | -| `start` | a delivering pipeline for a watched branch's head, by hand | the operator, the healers | -| `show`, `list`, `stands`, `why-carried`, `kept` | one pipeline whole with its step log; the pipelines and runs of a module, a branch or a commit; what stands still; where a result was carried from; the kept results | any person, an agent session, the healers | -| `review` | a code review's result for a commit and fingerprint | the reviewers the definition names; refused from the commit's author | -| `rerun`, `accept`, `cancel`, `ask-again` | one step again; a failed `optional` step accepted with why; a pipeline ended, its asks dropped; a declined `approval` asked again | the operator, the healers (`accept` and `ask-again` the operator) | - -**The seat `mesh-catalog`** gains, from milestone G's delivery, `register`, `policy`, `watch`, `unwatch` and -`registrations`, and the event `registration-changed` (*A module's registration*), and `catalog-revision` names its -watched branch in its subject. **The build seats** gain the verb -`steps` and the accepted kind `step`, and **the seat `mesh-wants`** the kind `step`, each saying `step-ended`. **The -seat `mesh-builds`**, new and held once for the mesh, says `package-published` (*A publish rebuilds*). - -**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 -each step says*. - -**The seat `node-engine`**'s event `running..` carries, from research 085's milestone, the module, its -fingerprint and the digest of its build's manifest, so the node-engine of another node and the grants part decide a -seat's pass from it (ADR 0660 §2; *Who holds a seat is applied by one rule*). No verb is added. - -**The seat `git`.** The seat `git` (design 26, *The git seat*), of the Delivery domain, defined in -`mesh-delivery-contracts`, holds no verb today, and the delivery parts and the gate reach the forge by naming the forge -module's own tools and events and guessing the shape of their answers (on 2026-11-09 one guess, an answer wrapped in a -`result` field, made 316 conditions stand, issue 1966). From research 085's milestone the seat's definition carries the -verbs and events below, their inputs and answers typed in its contract, served and said by the module that claims the -seat; every delivery part and the gate call and hear the seat, never the forge module by name (acceptance test 27). -They are this design's choice under ADR 0596 §1, §2 (a module's events are subscribed to and its data asked through -its seat verbs) and ADR 0625 §1 (a seat's contract is generated from its definition). The table is built from what the -parts' manifests declare in `invokes` and `consumes` and what their code calls, read on main on 2026-11-09 (mesh-delivery -042b8d1f, mesh-catalog's gate); a declaration nothing calls (the checker's `gitea_commits`, `gitea_compare`, -`gitea_list_branches`) gets no verb and is dropped from its manifest: - -| Verb | Answers or does | Called by (today's call) | -|---|---|---| -| `pull` | one pull request, by its repository and number or by its merge commit: number, base, head branch, head, merge commit, author, state, and `merging` while the forge merges it; an unknown one answers the error kind `not-found` | the forge bridge (a merge the gate did not make; `gitea_api`), the gate (a stalled line's author) | -| `branch` | from milestone G's delivery: a branch's head, and whether a protection rule refuses a direct push and admits only merges | the indexer (`register`, `watch`), the forge bridge (a watched branch's head) | -| `open-pulls` | every open pull request of every repository, its head and base, the statuses its base requires and those its head has | the forge bridge at its start and each reconnect (ADR 0443 §5), the indexer, the gate (`gitea_open_pulls`) | -| `tags` | a repository's tags, paged, the page's bound said when more exist | the forge bridge, for the builder's floor (`gitea_api`) | -| `trees` | the trees of one commit | the forge bridge, the checker, the indexer (`gitea_commit_trees`) | -| `file` | one file at one commit | the forge bridge, the checker, the indexer (`gitea_get_file`) | -| `contains` | whether one commit contains another | the indexer, the gate (`gitea_contains`) | -| `statuses` | the statuses a commit carries | the gate (`gitea_commit_statuses`) | -| `status`, `comment`, `view`, `note` | a commit status, a comment, the delivery's one view comment, a line on the commit's note under `refs/notes/mesh-plan`; a comment and a note line carry the act's id and an id already held is skipped | the forge bridge alone, performing the gate's and the checker's `owed` (`gitea_commit_status`, `gitea_add_comment`, `gitea_delivery_view`, `gitea_note_append`) | -| `merge-pull` | until G's delivery: merges a pull request at the head the operator's Approve names; refuses a head that moved, a branch no rule guards, a head the Approve does not name and a merge git refuses; judges no check (below). From G's delivery: merges with the forge tools' account (ADR 0398 §2) at the head the caller names, judging that it is still the pull request's head and that the branch's protection refuses a direct push, the protection read again on every `branch-moved`, and refusing a head when any run at its build-input fingerprint failed a `required` step, judged from its own state `failed-steps` heard from the runner's `step-ended`, never by a call; a head with no run yet or a passed one merges, it never waits for a run and never asks an Approve (ADR 0666 §11). A pull request already merged at that head is answered performed | until G's delivery the forge bridge alone (`gitea_merge_pull_request`); from it any session the tool runner proves, and the operator | - -| Event of the seat | Says | Heard by (today's event) | -|---|---|---| -| `pull.updated`, `pull.merged`, `pull.closed` | a pull request's new head, its merge with the merge commit, its close | the forge bridge, the gate (`gitea.pull.*`) | -| `forge.pull`, `forge.status`, `forge.delete` | the forge's own webhook of a pull request, of a commit status, of a deleted branch | the gate (`forge.pull`, `forge.status`), the forge bridge (`forge.delete`) (`gitea.forge.*`) | -| `repo.writable` | a repository the forge can write again | the gate (`gitea.repo.writable`) | -| `branch-moved` | from milestone G's delivery: a branch's new head, by a merge or an admitted push, from the forge's push webhook | the forge bridge | - -Each is said on `mesh.seat.git.event.`, declared in the seat's definition, and heard through the hearer's -`consumes` of the seat. - -- **A failed read answers an error, never an empty answer.** When the forge does not answer, refuses or answers what - the contract cannot type, the verb answers an error naming why; an empty list or an absent file is answered only - when the forge said so. A caller never reads an error as "none". How it is checked: the seat's fixtures hold an - error answer for each verb, and each caller's unit table has a row for it (the forge bridge, the checker, the - indexer, the gate). -- **Who may call an act.** The holder's trusted config value `callers`, which today names per verb who may call the - two package seats' verbs (design 51), gains a line per verb of the seat `git`: the act verbs name the forge bridge - alone, the reads name each part that declares them in `invokes`; the grants part grants by it, and the holder - refuses a caller it does not name. From G's delivery the callers per verb are the seat definition's field `callers`, - the grants part's one source (ADR 0666 §20): `status`, `comment`, `view` and `note` the forge bridge alone, - `merge-pull` the persons and agent sessions the tool runner proves, the reads the parts that declare them. The forge module's own acting tools (`gitea_merge_pull_request`, - `gitea_commit_status`, `gitea_delivery_view`, `gitea_note_append`, `gitea_add_comment`) retire with the milestone, - and so do its watch of the checker's state `owed` and its read of the gate's `source`, so the bridge stays the one - part that acts on the forge. How it is checked: test 51 reads the grants, not the code. -- **`merge-pull` judges no check, and from G's delivery no Approve, only the runs at the head's fingerprint.** The operator's answer that no check gates delivery (Telegram, 2026-11-09 01:11 UTC, - "Remove them in G") covers the forge module's own setting `check-gates-merge` as well as the gate's: both are - removed, and `merge-pull` merges an approved head whatever its `mesh/check` says. The record that ends ADR 0551 and - ADR 0548 §2 to §4, §9 and §10 also changes ADR 0534 §4's judgement of `mesh/check` by the merge; it is to be accepted - before the push (*Nothing merges into a repository under check*). From G's delivery it judges no Approve either, its caller - is any session the tool runner proves, and it refuses a head when any run at its fingerprint failed a `required` step, - never waiting for one (ADR 0666 §11; *Approval is a step, and a session merges a pull request*). The reads the - gate would have needed to merge (the forge's word for a conflict, the base's required statuses, the merging account's - write access) are not added: no part of the mesh but `merge-pull` merges, and it answers git's own refusal of a merge - as refused. -- **The order is the module graph's, and each step has its way back.** The verbs are a served-verb edge (ADR 0614): - the forge module's new build runs before the forge bridge's, and the forge bridge's before the gate's, since the - bridge calls the verbs and performs the gate's `owed`, and the gate says `owed` only to a bridge that hears it; all - three are ahead of the acting parts' steps, which read through the seat. While the new forge runs and the old bridge - still calls the retired acting tools, the old bridge's acts fail loudly and stay unperformed in the old gate's keys, - and the switch carries them (*What the gate owes the forge*). A revert goes back in the other order: the gate, then - the bridge, then the forge, each wanted only once the one after it in the forward order runs its reverted build, so - no part calls a verb nobody serves. ADR 0656 §3 stands beside it: the acting parts' new versions act first in their - own steps. Two things cover the gap while the forge is still on its old build and serves no verb of the seat: the - indexer starts on its kept definitions (below), and a check whose read through the seat `git` fails says `could not - run`, naming the forge, and is asked again on the forge module's `running` (*A check runs to its end*). -- **A part that cannot read the forge keeps what it has, and says so.** The indexer, at its start, waits for the - seat `git` at most 2 minutes; with no answer it judges from the last definitions it kept, says in its - `_first_views` that it waits for `git` and since when, writes `view-waits` after five minutes, and reads the - definitions once the seat answers; it then judges again every manifest it judged from kept definitions. Two minutes is longer than a forge's restart, and short enough that a start is - not held behind a forge down for good. How it is checked: test 54. -- **How it is checked.** The seat definition's conformance fixtures hold one answer and one error of each verb and one - of each event, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's, the - indexer's and the gate's unit fakes answer from the same fixtures; a call of an act verb from an account other than - the forge bridge's is refused (test 51); live, a search of the delivery parts' and the gate's code finds no name of - the forge module's tools or events. - -**Links of the trace.** No part reads a review line (ADR 0659 §1 to §3); who may review and the review verb's grant -stay issue 1782's, outside milestone G, and that verb gates nothing (ADR 0659 §2). The trace's links are this -design's choices under ADR 0656, so that one call traces research to the running build -with no text match (research 085, Findings 7). The issue tracker gains link kinds from an issue to the decision and to the research effort -it carries, made by a tool call and never by text, as ADR 0493 §4 has every link made; the knowledge base gains a -typed link from a decision to the milestone of the issue tracker that builds it, made by a tool call, which joins the -link kinds of its link-fits rule (design 55), beside the `became` link a research effort gets at its graduation -(playbook 02). How it is checked: whole (gate), one ask per milestone, withdrawn by a new head; -a waiter on one member's pull request wakes only on that member's transition. Whole (issue tracker, knowledge base): -an issue linked to a decision and to a research effort reads both back; a decision linked to its milestone reads it -back. Live: research 085's milestone's record reads back research 085 to its decisions, its milestone, its issues and -its pull requests with no text match. - -**A session hears that a record was accepted** (issue 595). The knowledge base already says, on its holder's own -stream, the event `reviewed` for every judgement of a record; from milestone G that event carries, when the operator's -Approve accepts a record, that it was accepted and by the operator, with the warrant, and the tool runner's account is -granted to hear it, so a session waiting on a record wakes on it. The design's choice under ADR 0596 §1 (a module's -events are subscribed to): a new event of the seat `knowledge-base` would be a second path for the same fact (lead-47, -2026-11-09). How it is checked: in the knowledge base's whole-module test an Approve that accepts a record says -`reviewed` with what was accepted and by whom, and the tool runner's account hears it. +From research 085's milestone and milestone G's delivery the seat gains verbs and events, and the seats +`mesh-pipeline-runner`, `mesh-catalog`, `mesh-wants`, `mesh-builds`, `node-engine` and `git` theirs: design 47 G, +*The verbs milestone G adds*. **The status view's tools** read the keys and change nothing: `assigner_wants`, `assigner_want`, `assigner_differences` and `node_haves`, beside the indexer's and the builder's. Its events are `delivered`, @@ -3663,7 +1909,8 @@ ADR 0667's checks, of milestone G itself: that branch passes it (whole, runner and condition keeper). 99. **The build capacity.** In the week after G, the build nodes' time per day, read from the build modules' step keys and the builder's build log, is set beside the checker's in its last week; a newer publish supersedes an older one's - rebuild pipelines, and the first pipelines after G carry what the checker's kept verdicts held (live; whole, runner). + rebuild pipelines, and the first pipelines after G run every module's tests once, `kept` starting empty, and carry + from then on (live; whole, runner). 100. **The new assigner before the new merge.** In G's delivery the delivery plan delivers the new assigner before the forge module's new `merge-pull` and before the runner, by their `needs`, and no merge made through the new `merge-pull` reaches an old assigner; until the first registrations' `registration-changed` the new assigner writes @@ -3716,27 +1963,7 @@ ADR 0667's checks, of milestone G itself: view's events instead; the assigner's comparison with the send; the node-engine's queue of whole declarations, its orders, put-backs and five-minute reconcile; the buckets of plan state; the stop of a shared plan (ADR 0299, superseded by ADR 0539 §18); and a declaration sent by hand (ADR 0526 §4, changed by ADR 0539 §10). -- At research 085's milestone's delivery: the indexer's map from language to build module, and the checker's own - module names and its cases for a change to the manifest validator and to the Go build module (ADR 0656); a build - module's edge as a trigger, with the extra build per recipe change (ADR 0603 §3, as ADR 0656 changes it); composing - from manifests a module whose changed build module the check cannot run (ADR 0603 §10 and ADR 0614 §7, as ADR 0656 - changes them); the module graph's one key per module with five past values as what parts read (ADR 0603 §2, as - ADR 0656 changes it); the tiers worked out from five live sources (ADR 0603 §6, as ADR 0656 changes it); a seat - list compiled into a part, and every read of another module's bus state the revision view replaces (ADR 0656); the - gate's `merges`, carried into the delivery records and written no more, and its `owed`, said as events; the hold, - the merge turns, their stalled line and tools and the setting `check-gates-merge`, once their record is accepted; - the last three reads of a pull request's description (ADR 0659 §4); every delivery part's call of the forge module's - own tools, for the seat `git`'s verbs; and the four seat-list readers the delivery parts wire by hand. -- At milestone G's delivery, from research 086 (ADR 0666, ADR 0667): the checker (`mesh-delivery-checker`), its seat, - its try with `tryevent` and the indexer's try candidates, its kept verdicts and carries (the runner's from then), the - checking program and `mesh/check`, and its buckets and bus account, removed with the gate's forge merge token only - after the first pipeline after G's delivery has delivered a change end to end and on the operator's one word, its - buckets through `drop` after a snapshot whose restore is tested (ADR 0667 §12); the bundle of steps run before a merge; the Approve per - merge, the milestone ask and its three events, and the gate's merges; the gate's merge states, one key per pull request and its verbs `table`, `recheck` and - `close`; the trunk-only want; `source.main`, for one source revision per watched branch; every fixed branch, - repository and module name in code logic; the recovery merge verb this design sketched, never built; and the holders' - trusted `callers` values, for the seat definitions' `callers`. The words **merge check**, **try**, **trunk commit** - and **merge turn** go to *Not:* lines with the as-is update after that delivery, as the glossary's entries say. +- At research 085's milestone's delivery and at milestone G's: design 47 G, *What retires at milestone G*. ## Milestones diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md new file mode 100644 index 00000000..b1c606d5 --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -0,0 +1,2101 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-10 +decisions: + - "ADR 0668" + - "ADR 0667" + - "ADR 0666" + - "ADR 0664" + - "ADR 0663" + - "ADR 0662" + - "ADR 0661" + - "ADR 0660" + - "ADR 0659" + - "ADR 0656" + - "ADR 0648" + - "ADR 0646" + - "ADR 0645" + - "ADR 0644" + - "ADR 0641" + - "ADR 0639" + - "ADR 0636" + - "ADR 0635" + - "ADR 0633" + - "ADR 0625" + - "ADR 0621" + - "ADR 0619" + - "ADR 0618" + - "ADR 0615" + - "ADR 0614" + - "ADR 0603" + - "ADR 0597" + - "ADR 0596" + - "ADR 0595" + - "ADR 0593" + - "ADR 0584" + - "ADR 0573" + - "ADR 0571" + - "ADR 0567" + - "ADR 0564" + - "ADR 0563" + - "ADR 0551" + - "ADR 0548" + - "ADR 0539" + - "ADR 0534" + - "ADR 0525" + - "ADR 0505" + - "ADR 0498" + - "ADR 0493" + - "ADR 0448" + - "ADR 0443" + - "ADR 0441" + - "ADR 0437" + - "ADR 0429" + - "ADR 0421" + - "ADR 0404" + - "ADR 0398" + - "ADR 0391" + - "ADR 0363" + - "ADR 0283" + - "ADR 0240" + - "ADR 0235" + - "ADR 0233" +--- +# 47, milestone G — every line milestone G builds + +This is milestone G's part of [design 47](../47-delivery-from-commit-to-delivered.md): the pipelines per module of +research 086 and the two graphs of research 085, one milestone (ADR 0667 §1). The main file keeps the purpose, the +whole shape, the acceptance tests and the milestones; every line milestone G builds is here, moved whole from the main +file on 2026-11-10, and is cited as "design 47 G §n". A sentence that holds a delivered line and G's change together +stayed in the main file, and G builds what it says from G's delivery. A section named in italics is in this part, or, +when it is not here, in the main file. + +## Pipelines per module + +*ADR 0666, ADR 0667; ADR 0595, ADR 0596, ADR 0448 §2, §4, ADR 0548 §5, ADR 0564 §2, §4, ADR 0283, ADR 0656 §4, §6, ADR 0633 §5, +ADR 0664 §4, ADR 0398 §2, ADR 0593 §9; research 086, Direction.* + +§1 From milestone G's delivery. A change of any module, the delivery system's own parts included, goes from a commit to +the machines through that module's **pipeline**: the pipeline steps its **policy** names, run for each commit or group +on each of the module's **watched branches** by the seat `mesh-pipeline-runner`, ordered by the catalog graph's edges +between artifacts, with no hand act (ADR 0666 §1). Milestone G is the one milestone that builds it, beside the two +graphs, the revision view and the delivery record (ADR 0667 §1). Until G's delivery the sections this one replaces +(*Checks*, *A try*, *The Approve and the merge*, the state table and the trunk-only want) hold as written, and they +check and deliver milestone G itself (ADR 0667 §2); each of them says so in a line of its own. + +``` + a session ──merge-pull (seat git)──► forge ──commit on a watched branch──► forge bridge ──► indexer (mesh-catalog) + │ registrations; a catalog graph + │ per watched branch; the revision + │ validated before take-in + ▼ take-in.taken + pipeline runner (mesh-pipeline-runner) ── asks each pipeline step on the serving seat's work queue, hears step-ended: + │ lint, unit tests, integration tests ──► the build seats (node-build-go, node-build-node, node-build-image) + │ composition ──────────────────────────► the assigner (mesh-wants) + │ code review ──────────────────────────► the runner's own verb `review`, from the reviewer's bus account + │ approval ─────────────────────────────► operator-channel: accepted kind ask, then decided + ▼ pipeline-ended (every step's result), keyed by source revision + assigner ──► wants from a revision whose required steps passed, on the watched branch a placement chose + gate ──► the delivery record keeps the step and pipeline events +``` + +### A module's registration + +- §2 **What it holds.** The seat `mesh-catalog` keeps each module's **registration**: its git origin, the path of its + manifest, its watched branches and its policy, on the bus, every version with who changed it and the warrant of the + operator's Approve (ADR 0666 §2). The indexer is a foundation part, so the registrations are kept in its old sections + (ADR 0619 §1 to §3, ADR 0666 §4): one key per module in its old `state` section `registrations`, and every version on + its old `streams` section's stream `registration-history`, kept with no age limit. + The catalog graph is derived from the registrations and the manifests they point to; the mesh knows modules, and a + repository only as a module's git origin. +- §3 **Changing one is a trusted act.** The seat's verbs `register` (a module whole), `policy` (one module's step + statuses, per artifact), `watch` and `unwatch` (add or remove a watched branch) each raise an authorising ask of tier + approve through the seat `operator-channel` (design 46 §10), naming every value the change moves, one line each + ("`mesh-grants:program` unit tests: `required` → `bypass`"), and write the version only on its warrant; a Decline or + an expiry writes nothing (ADR 0666 §2). Until a record binds each forge account to its bus identity, `register` and + `policy` refuse code review as a `required` step, naming why, so no review gates while its author cannot be told + (ADR 0666 §22; issue 1966, note 157). A manifest moved to another path is the one manifest change that changes the + registration; any other manifest change is a commit, judged at its revision. +- §4 **Said and read.** Each written change is said as the seat's event `registration-changed` (the module, the version, + what moved); the verb `registrations` answers every registration, or one with its history. The indexer derives the + modules it keeps from them, and the forge bridge its watched branches, each by a first view of `registrations` and + then that event, never by reading the ledger (ADR 0666 §3, ADR 0596). +- §5 **An unprotected branch is refused.** `register` and `watch` read a branch's protection through the seat `git`'s read + verb `branch` (its head, and whether a rule refuses a direct push and admits only merges), and refuse a watched branch + the forge does not protect, naming it (ADR 0666 §3). +- §6 **The foundation parts are the manifest validator's list.** The manifest validator's list (ADR 0619 §3) holds + exactly the foundation parts, ADR 0619 §1's and the runner, which joins them (ADR 0666 §4); the checker leaves the + list when it is removed (*How milestone G itself is checked and delivered*). A registration holds no foundation + value. How it is checked: a unit test of the manifest validator refuses a template in the manifest of every part on + its list, the runner's included, and judges every other part's manifest as any part's. +- §7 **No part fixes a name.** Every branch, repository and module name a part needs is read from the registrations and the + catalog graph; the lint step refuses such a name in a string, a constant or a table of code logic, a Go import path + not being code logic (ADR 0666 §5). About 39 such names in 9 parts were found on 2026-11-09 (research 086, Findings); + milestone G removes them. +- §8 **The first registrations are one ask.** At its first start on G's build the indexer, holding no registration, + proposes one registration for every module its trunk revision holds, from what it took in (the origin and path of + each), its watched branch the repository's default branch read through the seat `git`, and the statuses the check + judged until then: lint, unit tests, integration tests and composition `required`, code review `optional`, approval + `bypass`, and `bypass` for every step the artifact's build seat's `steps` does not answer at the seed, so no module + is held by a `required` step it has nothing for (the design's choice under ADR 0666 §7, 2026-11-10); it raises them as one ask naming every registration, as the seat `mesh-module-assigner`'s `adopt` proposes + placement whole (ADR 0567). Until that Approve the runner starts no delivering pipeline and says why, so the + operator is told of this one ask before G's delivery starts. A Decline or an expiry leaves the seed unwritten and + says so; the indexer's tool `indexer_seed_again` raises the ask again, recorded in the hand-act log. +- §9 **Kept for the life of the mesh**, never derived again (research 086, choice 25): the history stream's cap is 64 MiB. + About 120 registrations of about 1 KiB, and an estimated change a week each (a guess, not a measure), grow about 6 MiB + a year, so the cap holds about ten years; past 80 % the indexer says `filling`, as every store of *Two graphs, one + source of truth* does. +- §10 **How it is checked.** Whole (indexer): an unprotected watched branch is refused, naming it; a policy change without + the warrant writes nothing, and a declined one writes nothing; a change from `required` to `bypass` asks, showing + that line; `registration-changed` is said and `registrations` answers the history; the first start proposes the seed + as one ask, and `indexer_seed_again` asks it again after a Decline and after an expiry; a policy making code review + `required` is refused. Unit (the Go build module's lint step): a module name in a string, a constant and a table is + refused, an import path is not. Tests 75, 68 and 83. + +### Watched branches and the catalog graph per branch + +- §11 **Only a watched branch starts a pipeline.** The forge bridge hears every commit of every watched branch the + registrations name, through the seat `git`'s event `branch-moved` (a branch's new head, from the forge's push + webhook, which a merge and an admitted push alike say), and takes the commit from the forge's own record by the seat's + verb `branch`, never from the event's body (ADR 0666 §6). One event for every way a branch moves, because a merge is + not the only one. `branch-moved` is kept on the seat's stream `SEAT_git_branch-moved`, which the bridge hears through + a durable consumer, so a move said while the bridge is down is heard when it starts; at its start it also reads each + watched branch's head the same way. +- §12 **A head with no pipeline is a finding.** A watched branch's head that starts no pipeline within five minutes is the + watchman's finding `pipeline-stands`, naming the branch and its head, raised by the condition keeper (ADR 0564 §2), + unless it waits as progress not owed: a member of a gathered group is governed by ADR 0633 §5's 30-minute bound, and + a merge behind a pending revision of the same branch by its 60-minute bound (ADR 0666 §24). The finding clears on the + runner's `pipeline-started` for that head. +- §13 **The catalog graph per watched branch.** The indexer keeps the catalog graph once per watched branch, from that + branch's manifests at each source revision (ADR 0666 §6): each revision names its branch, and the stores, caps and + pins of *Two graphs, one source of truth* hold per branch. +- §14 **A placement names the branch.** Each module of a placement key names the watched branch its node runs, by default the + registration's first, set by a placement proposal like every other placement change (ADR 0567, ADR 0666 §6). The + assigner computes each node's entries from the catalog graph of the branch its placement names, so a module watched on + `main` and `beta` runs `beta` only where a placement chose it. +- §15 **A source revision per watched branch.** The gate keeps one source revision per watched branch, `source.`, + in place of `source.main` (ADR 0429 §11 to §13 as ADR 0666 §6 changes them), in the old `state` section `source` + already holds, its key's layout being the only change. The gate is no foundation part: the bus module's delivery + waits on no act of the gate's, since the indexer takes a revision in from the forge bridge's stream and the assigner + writes wants from the runner's events, so the gate's other kept data stay in ADR 0595's templates (ADR 0666 §4), and serves it by the seat + `mesh-delivery`'s verb `source` with the branch, saying `source-moved` on each new revision; the indexer and the + node-engine's catch-up read it there, never the gate's state (ADR 0596). Each want names a build-input fingerprint + computed at one such revision. +- §16 **Validated before any take-in, on every branch.** Before it takes a revision in, on every watched branch, the running + indexer asks the manifest validator built from the revision's heads to judge it in the build seat's sandbox (the + ask `run`, *What ADR 0656 leaves to this design*). A revision it refuses stays pending under ADR 0633 §5's + 60-minute bound, its hand acts and its condition `take-in-waits`; the way back is a revert, an ordinary merge + (ADR 0666 §6, §26). A revision on the branch the indexer's own placement chose is taken in only by the indexer it + names (ADR 0633 §1). This validation is no pipeline step and waits on no pipeline, so every delivering pipeline runs + on a revision already taken in. +- §17 **A branch whose protection was removed starts nothing.** On every `branch-moved` the forge bridge reads the + branch's protection again; a move of a watched branch whose protection was removed starts no delivering pipeline, and + the forge bridge writes the finding `branch-unprotected`, naming the branch, which the condition keeper raises and + clears once the protection is back (ADR 0666 §6). +- §18 **A pending revision holds its own branch only.** A merge waits behind a pending revision only on the same watched + branch; other branches move on (ADR 0666 §6, which changes ADR 0633 §3). +- §19 **A refused new indexer holds its branch until a revert lands.** A merge of a new indexer whose own validator refuses + its revision has already landed when it is refused, since the validation runs before take-in and after the merge + (ADR 0666 §6). It stays pending, and every later merge on that watched branch waits behind it, said by + `take-in-waits` at the 60-minute bound, until a revert, an ordinary merge through its pipeline (ADR 0666 §26), lands + and is taken in; nothing takes a later merge in past it. This is the cost of validating every revision with the + validator built from its own heads, the design's reading of ADR 0666 §6, and the operator is told it with the + condition, which names the pending revision and the revert as the way on. +- §20 **How it is checked.** Whole (forge bridge): a commit of a branch no registration names starts nothing; a commit on a + watched branch is taken from the seat's `branch` read, not the event; a head missed while the bridge was stopped is + read at its start. Whole (indexer, assigner): a commit on `beta` gives a revision of the `beta` graph only and moves + only the want of the node whose placement chose `beta`; a revision whose own validator refuses it stays pending and + holds `beta` while `main` moves on. Whole (watchman): a head with no pipeline for five minutes raises + `pipeline-stands`, cleared by `pipeline-started` for that head, and a gathered member does not; a move said while the + bridge was stopped is heard from the seat's stream. Tests 64, 68, 76, 78 and 82. + +### Pipeline steps and the policy + +- §21 **The runner.** The seat `mesh-pipeline-runner`, held once for the mesh by the module `mesh-delivery-pipeline`, + runs each pipeline (ADR 0666 §7). It is a foundation part, since the bus module's own delivery waits on its pipeline + (ADR 0666 §4): its kept data are in old sections (ADR 0619 §2), it waits on no bus module process to start, and it + joins the manifest validator's list of foundation parts, which refuses its manifest asking a template (ADR 0666 §4, + which changes ADR 0619 §1 and §3). It reads the policy from the registrations, asks each step that is not `bypass` of + the seat that serves it, and says each step's start and end and the pipeline's end (*Health and going back*, below). +- §22 **The steps and who serves them.** Lint, unit tests and integration tests are the build seats' (`node-build-go`, + `node-build-node`, `node-build-image`): each gains the verb `steps`, which answers which pipeline steps an artifact + can run by its language's convention (as *The check from release 4* says the tests are found), and the accepted kind + `step` on its stream `check`, which runs one. Composition is the assigner's, an accepted kind `step` of the seat + `mesh-wants`. Code review comes in through the runner's own verb `review`. Approval is asked as the seat + `operator-channel`'s accepted kind `ask`, on its work queue by the runner's name (design 46 §10, *Asks, warrants and answers on the bus*; + the seat has no verb `ask`), and heard on its event `decided` (ADR 0666 §7, §10). A step with nothing to do ends at once as + `nothing to run`, never as passed. +- §23 **The policy.** Each step's status, per module and per artifact, is `required` (runs; holds the delivery until it + passes), `optional` (runs; a failure is shown and may be accepted) or `bypass` (nothing runs; said as bypassed), + kept in the registration and never in a manifest: the manifest validator refuses a manifest that names a status + (ADR 0666 §7; research 086, R3). A `required` step that ends `nothing to run` holds the delivery and names the step. +- §24 **Asked on a work queue, ended by an event.** The runner asks each step on the serving seat's work queue and hears + its end as that seat's event `step-ended` (the record's `step ended`), on + `mesh.seat..event.step-ended.`, the node that ran it in the subject and each holder's account granted only + its own node's subject, through a durable consumer of its own, so a step outlives a restart of either side (ADR 0666 + §7, ADR 0363 §2). A build seat's holder restarted mid-step gets the step again from its work queue and finishes it + (ADR 0664 §1). A unit or integration test step says its test counts in `step-ended`, and `pipeline-ended` names any + repository over 50 integration or 500 unit tests, and any catalog module over them (ADR 0666 §8; the test budget). A step's outcome is one of `passed`, `failed`, `nothing to run`, + `bypassed`, `carried` (with the pipeline it was carried from), `could not run` (naming the part not heard), + `waiting on `, `accepted` (a failed `optional` step a person accepted, with who and why), `superseded` and + `cancelled`. +- §25 **Carried, not run.** A step whose build-input fingerprint and policy did not change is carried by its package + digests, as ADR 0548 §5 holds, the runner making the carry instead of the checker: a test step runs only the packages + with no kept result for their digest, and a run on request's results are reused when the merged commit's inputs match + (ADR 0666 §8; research 086, choice 24). An `approval` step is never carried. +- §26 **Approval comes last.** The runner asks the `approval` step only once every other `required` step of the pipeline + passed, so the operator approves a commit the mesh already judged, the one place ADR 0661 §3 had the check gate the + ask. A failure after an approval is said, as a failed `optional` step is, and the approval stays. +- §27 **Kept on the bus, in old sections.** The runner keeps each pipeline's state in its old `state` section `pipelines`, + one key per pipeline keyed by its watched branch, source revision and module, written by compare-and-set so a holder + replaced mid-pipeline and its successor never both write one; its step log on its old `streams` section's stream + `step-log`; each step's output in its old `objects` section `step-outputs`, named in the log; and each package's + passing result per level in its old `state` section `kept`, under the digest of what its tests read (ADR 0666 §4, §8; + *The runner's kept data*, below). `kept` starts empty: the runner takes no verdict of the checker's, since a step's + result is taken only from the bus account of that step's seat holder (ADR 0666 §21), so the first pipelines after G + run each package's tests once and carry from then on (the design's choice, 2026-11-10). +- §28 **How it is checked.** Whole (runner): a step with unchanged fingerprint and policy is carried and says from where, + and an `approval` step is not; a module with no integration tests shows `nothing to run`, and a `required` unit-test + step on a module with no tests holds its delivery; a bypassed step is said; a runner restarted mid-step hears the + step's end and finishes the pipeline; two runners writing one pipeline key, one loses by compare-and-set; a test step's + counts are in `step-ended` and a repository over budget is named in `pipeline-ended`. Whole (Go build module): `steps` + answers the steps of a fixture artifact, a `step` ask ends with `step-ended` on its own node's subject, one on another + node's subject is refused by the bus, and a build module restarted mid-step finishes it. Unit (manifest validator): a manifest naming a status is refused. Tests 65, 66, 73 + and 88. + +### What holds a delivery + +- §29 **The assigner writes a want only from a passed pipeline.** The runner says each pipeline's end as `pipeline-ended` + (the record's `pipeline ended`), keyed by source revision, with every step's result. The assigner consumes it, and + writes a want only from a source revision whose every `required` step passed for the modules it moves, on the watched + branch a placement chose, by compare-and-set on a newer revision, so an older revision ending after a newer one + writes nothing (ADR 0666 §9). The compare-and-set is keyed per module and watched branch: it compares a revision only + with the last one written for the same module on the same watched branch, so a revision of `beta` ending after a + newer one of `main` writes its own want and refuses neither (the design's choice under ADR 0666 §9: source + revisions are kept per watched branch, `source.`, and two branches' revisions have no order between them). This is one more rule beside the assigner's others (ADR 0429 §6), checked per module. + From G's delivery it replaces the trunk-only want (ADR 0656 §2 as ADR 0666 §9 changes it). For a group's source + revision the runner says one `group-ended` once every member's modules have ended, and the assigner writes the + group's wants only on it: a member's own `pipeline-ended` writes nothing (ADR 0666 §9). +- §30 **A failed `optional` step holds nothing.** It is shown in `pipeline-ended`, in the delivery record and in the runner's + `show`; a person may accept it by the runner's verb `accept`, recorded in the hand-act log with who and why. +- §31 **A newer commit supersedes an older pipeline of a module whose fingerprint it changed.** On a newer commit of the + same watched branch the runner ends a module's older running pipeline as `superseded` only when the newer commit + changed that module's build-input fingerprint (ADR 0666 §9); a module the commit leaves as it was finishes its + pipeline. A newer publish of a package likewise supersedes the rebuild pipelines an older publish of it started. A + superseded pipeline's waiting asks are dropped by each build seat's `drop` (ADR 0664 §4), naming + the newer commit as the cause, so a cancelled pipeline leaves no ask on a stream (a gap G's prepare found: a cancelled + build withdrew only the builder's wait). +- §32 **A step nothing starts ends its pipeline.** A step whose prerequisite nothing starts (a seat with no holder, a + prerequisite step `bypass`) ends the pipeline refused within five minutes, naming the step and what it waits for, + never as a silent wait (research 086, R5); a step whose seat has no holder ends its pipeline failed, naming the seat + (ADR 0666 §17). +- §33 **A failed head is said to its author, then to the operator.** A `required` step that failed, or ended `could not + run`, on a watched branch's head is the runner's finding `pipeline-failed`, naming the module, the branch, the commit + and the step; the condition keeper raises it as the commit's author's for two hours and then the operator's, as ADR + 0505 §5 and §6 have a delivery whose cure is its author's new head, and clears it when a newer revision of that branch + passes that step (ADR 0666 §24). The author is the forge's record of the commit, read through the seat `git`. +- §34 **The delivery record keeps every step.** The gate's delivery record consumes the step and pipeline events and keeps + them as long as the record (ADR 0666 §9; *The delivery record*). +- §35 **How it is checked.** Whole (assigner): a failed `required` step writes no want; a failed `optional` step holds + nothing; an older revision's `pipeline-ended` after a newer one's writes nothing, and a revision of one watched branch + ending after a newer one of another writes its own want; a group with one member passed and + another still running writes no want until `group-ended`. Whole (runner): a newer commit that changed a module's + fingerprint supersedes its older pipeline and drops its asks, and one that left it unchanged lets it finish; a step + with no holder ends the pipeline naming the seat within five minutes; a failed `required` step on a head raises + `pipeline-failed`, the author's for two hours and then the operator's, cleared by a newer passing revision. Tests 66, + 67, 87, 89 and 98. + +### Approval is a step, and a session merges a pull request + +- §36 **No Approve per merge.** The operator's approval is the pipeline step `approval`, its status in each module's policy; + no Approve is asked per merge, and the delivery gate's merge turns and its Approve per merge go (ADR 0666 §10, which + changes ADR 0661 §3 to §5). The operator is asked only where a module's policy says `approval`, and for registrations + and policy changes. +- §37 **The approval is an ask of the runner's.** The runner raises an authorising ask of tier approve as the seat + `operator-channel`'s accepted kind `ask`, under its own name, its `about` the module and the watched branch only, so a newer commit's ask + replaces the older one's (design 46 §10). The warrant ends the step passed; an ask that is declined, expires or is + refused ends it `declined`, and one that is replaced ends it `superseded`. Only a declined `approval` step is asked + again, by the runner's verb `ask-again` (ADR 0666 §10). While it waits the runner + says `wait-held` waiting on the operator, never a stall (ADR 0283). +- §38 **A session merges a pull request.** Any session the tool runner proves, and the operator, merges by calling the seat + `git`'s verb `merge-pull` over the mesh, which merges with the forge tools' account (ADR 0398 §2) at the head the + caller names. It judges that this is still the pull request's head and that the branch's protection refuses a direct + push, the protection read again on every `branch-moved` of the branch, and it judges the head's runs by its modules' + policies: it refuses a head when any run at its build-input fingerprint failed a step a policy marks `required`, and + merges a head whose `required` steps passed or that has no run yet, a failed `optional` or a `bypass` step refusing + nothing; it never waits for a run and never asks an Approve (ADR 0666 §11). The forge keeps only its branch protection. +- §39 **Judged from its own state, never by a call.** The holder of the seat `git` consumes the runner's `step-ended` + through a durable consumer of its own and keeps, in its own state `failed-steps`, each failed `required` step by + module and build-input fingerprint, with the commits it was seen at and the run that failed it, until none of those + commits is a watched branch's head or the head an open pull request names (*The runner's kept data*); so a head + merges while the runner is down, judged on what it already heard (ADR 0666 §11, ADR 0596). A head's fingerprints are + those its runs named for its commit; a step that failed at that fingerprint in any run, a group's or another commit's + of the same content included, refuses it, unless a later run of that same step at that fingerprint, asked again by + the operator or the healers (the runner's `rerun`), passed: that pass clears the failure from `failed-steps` (ADR 0666 + §11). The runner's `step-ended` says that the step was a rerun and the caller the runtime proved, and the forge module + clears a failure only for a rerun whose caller is the operator or a healer; a plain pass, or a rerun asked by anyone + else, clears nothing. A run still running is judged by the steps it has ended, since the merge never waits. A refusal names the run, the module and the step, so the session knows what to fix. The gate merges nothing, and its forge merge token is + removed with the checker's buckets and bus account, after the first pipeline after G's delivery has delivered a change + end to end and on the operator's word (ADR 0667 §12; *How milestone G itself is checked and delivered*); the forge bridge hears the landed commit as it hears every commit of a watched + branch. +- §40 **The operator-channel hides no ask.** It keeps design 46's three open asks per asker, never holds the newest back, + and replaces an open ask by the same asker's ask with the same `about` (ADR 0666 §12; design 46 §6). +- §41 **How it is checked.** Whole (forge module, forge bridge): a session's `merge-pull` at its head merges with no run yet, + and with a passed one, and asks no Approve; with the runner stopped a head merges; a head at whose fingerprint any + run failed a `required` step, a head that moved and a branch whose protection was removed since its last move are + refused, each naming why; a failed `optional` step refuses nothing. Whole (runner): an `approval` step is asked last, + a Decline and an expiry each end it `declined` and `ask-again` asks it once more, and a newer commit's ask replaces the + older and ends its step `superseded`. + Live: a declined approval and a fourth ask each reach the operator. Tests 71 and 91. + +### Groups + +- §42 **Formed from the branch name.** The gate forms a group from a shared branch name carrying its issue number, as it + does today without the fixed list of trunk names (ADR 0666 §13). +- §43 **A member reaches its group by landing.** A member has reached its group when its commit lands on its modules' + watched branch. The forge bridge sets the group on each landed member's commit, reading the members from the seat + `mesh-delivery`'s verb `groups`, and publishes `forge.group.merged` when that verb says every member has (ADR 0666 §13, + which changes ADR 0633 §4). +- §44 **One source revision, one pipeline per branch.** The indexer keeps landed members pending until the group's end and + takes them in together as one source revision of that watched branch, saying `take-in.taken`; the runner starts the + group's one pipeline per watched branch on that event, and a member of a gathered group starts no pipeline of its own + (ADR 0666 §13). + When one member's `required` step fails, none of the group's modules is delivered (research 086, R5). +- §45 **A group that never completes is said.** By ADR 0633 §5's 30-minute bound and its condition; `bridge_group_end_again` + says a group's end again from the gate's group verb, never the gate's `merges` state (ADR 0666 §13, which changes ADR + 0633 §5). +- §46 **How it is checked.** Whole (indexer, gate, forge bridge): a group of three repositories, its own packages included, + is one source revision and one pipeline; one member's failed `required` step delivers none; a group with a member that + never lands raises `take-in-waits` at 30 minutes. Tests 67 and 81. + +### Dependencies between artifacts + +- §47 **Six kinds of edge between artifact references.** The catalog graph's edges run between **artifact references**, + `:`, such as `mesh-grants:program`, of six kinds (ADR 0666 §14): + + | Kind | B before A | Derived from | + |---|---|---| + | 1 | B published before A builds | a package A's build source imports (ADR 0573 §3) | + | 2 | B published before A delivers | an artifact A's manifest fetches by reference at its start | + | 3 | B delivered before A builds | a build module A is built by, and the toolchain its agreed version names (ADR 0584 §1) | + | 4 | B delivered before A delivers | a provision A requires and B provides, bus state among them (ADR 0614 §5), and a format's reader (below) | + | 5 | B has acted for A's new version | a seat's acting rule (*What ADR 0656 leaves to this design*): a grant, a credential, a verdict, bus state | + | 6 | B runs a version that serves A | a verb or a manifest word A uses and B serves or reads (ADR 0614 §2, §4), the build seats' `kept-artifact` among them | + + Each kind is derived from what manifests and seat definitions already say, and declared per artifact in the manifest's + artifact entry, its field `needs`, only where nothing else says it. Today's edges run between modules, and only a + package edge names an artifact; at module level the tool runner and the grants part looked like a cycle, per artifact + they are a chain (`mesh-grants:program`, then `node-tools:runtime`, then `mesh-grants:tools`). +- §48 **Removal runs the other way.** When something goes away the order is reversed: a seat moved to a new holder has its + old holder released before the new one claims (ADR 0666 §14). +- §49 **The edges alone order.** The build and the delivery order by these edges alone; a cycle of delivery edges (kinds 2, + 4, 5, 6) is delivered as one step, and a cycle of build edges (kinds 1 and 3) is refused, naming every artifact on it + (ADR 0666 §14; research 086, R17). The cycle rule is the indexer's, in the catalog graph it keeps, so the checking + program's cycle rule goes with the checker. +- §50 **A format's reader first.** A format a running reader cannot read is preceded by its reader, an edge of kind 4 on + every watched branch (ADR 0666 §14): the indexer's manifest validator for a new field of a seat definition or a + manifest, and on each node the node-engine that reads a manifest's new fields before any manifest that writes them + (ADR 0667 §4). +- §51 **How it is checked.** Unit (indexer's graph): a chain per artifact where modules would cycle; two programs needing + each other's new verb are one step; two artifacts each needing the other's new package to build are refused, naming + both; a seat moved to a new holder releases the old first. Live: the tool runner and the grants part delivered in + the order `mesh-grants:program`, `node-tools:runtime`, `mesh-grants:tools`. Test 79. + +### A publish rebuilds + +- §52 **Numbered at publication.** The builder numbers a package's version when it is published, never only when a build + is claimed, and says the publication as the event `package-published` of its seat `mesh-builds` (ADR 0666 §15; design + 51, *The versions*). The seat is new, held once for the mesh, defined in `mesh-delivery-contracts`; the builder held + none until now, and its waits stay on its own stream in this milestone. +- §53 **Every dependent is rebuilt against it.** On `package-published` every artifact with a kind-1 edge to the package is + rebuilt against the new version, and the runner, which consumes the event, starts its pipeline; nothing else is + rebuilt, and a change to tests alone publishes nothing (ADR 0666 §15; research 086, R18). +- §54 **A second start writes nothing.** The runner starts a pipeline on two events, the indexer's `take-in.taken` (ADR + 0666 §13) and `package-published` (ADR 0666 §15). When both reach it for the same source revision and module, the + second start writes nothing: the runner writes a pipeline's key (watched branch, source revision and module, *The + runner's kept data*) by compare-and-set on its absence, so one revision and module has one pipeline. The design's + choice under ADR 0666 §13 and §15: the key the runner already keeps is the one place a start is judged, so neither + event's producer needs to know of the other. +- §55 **Unread packages refuse.** A build that cannot read the published packages refuses and names the unread state + (ADR 0666 §15). +- §56 **How it is checked.** Whole (builder, indexer): a mesh-sdk merge changing one package publishes one version, and only + the artifacts importing that package rebuild; a merge changing only tests rebuilds nothing; a build whose packages + cannot be read refuses naming the state. Whole (runner): a `take-in.taken` and a `package-published` for the same + source revision and module start one pipeline, the second writing nothing. Tests 70, 80 and 101. + +### Any size + +- §57 **A value over the limit is an object.** mesh-sdk's own small object store package (ADR 0595 §9) puts a value over + the bus's per-value limit in the object store beside its state and keeps the object's name in the key; a verb's reply or an event over the + per-message limit carries the object's name (ADR 0666 §16). A replace writes the new object and sets the key by + compare-and-set; the object it replaced stays, as one whose key was deleted does, listed as `key gone` since no key + names it, and only the drop tool removes either, on a person's word: the mesh removes kept data only through `drop` + and `recreate`, each on a person's word (ADR 0595 §4; ADR 0666 §16). +- §58 **One object store per state, granted to its owner.** Each state asked by the template `state` gets its object store + beside it, `-objects`, a template form of ADR 0595 that the bus module provides and the grants part grants to + the state's owner alone (ADR 0666 §16); a foundation part's large values go in an old `objects` section of its own. + Its size is the template's (ADR 0595 §3). An object store holds no space until a value is written to it, so the many + it adds reserve nothing; the few values over the limit (a delivery record, a catalog graph revision list, a test's + output) are estimated, as a guess, at under 1 GiB across the mesh. +- §59 **An object sent is readable by the modules it is for.** A reply's or an event's object is written to the sender's + **sent store**, `_sent-objects`: the bus module plans it, with no manifest field, for every module of the new + form that serves a verb or emits an event, and a foundation part declares it as an old `objects` section + `sent-objects` (ADR 0619 §2). Its cap is the objects template's (ADR 0595 §3), and it keeps an object 7 days, after + which the bus expires it. That age is transport, not kept data: an object sent exists only to carry one reply or one + event, read within its call's bound or its consumer's lag, so its expiry is not a removal of kept data that ADR 0595 + §4 holds to a person's word (the design's choice; 7 days is a guess at the longest a consumer lags, read from the + fill tools once G runs). A full sent store is the bus module's `full` and its condition (ADR 0595 §6), and the sender + then refuses the large reply or event loudly, naming the store. The grants part derives from the manifests a standing grant to read them: the + callers its seat definition names for the verbs it serves, and the consumers whose `consumes` declare its events; + no other module may read them (ADR 0666 §16). The grant follows the manifests, so it is written when they change, + never per object. A consumer's reader of + the form is delivered before its producer writes it, an edge of kind 4. +- §60 **Its tools come with the package.** The package serves each module that imports it `_state_objects` (the + objects of each state, with their size, the key that names each or `key gone`, and the fill against its cap), + `_state_objects_drop` (drop the objects whose key is gone, on a person's word as ADR 0595 §4 removes kept + data), `_sent_objects` (each sent object, its size, when it was sent and to which verb or event, its age + against the store's and the fill against its cap) and `_sent_objects_drop` (drop sent objects older than an + age a person names); each drop names who asked and is recorded in the hand-act log. +- §61 **Its cost.** Only the modules that import the package are built with it, so no other module is rebuilt for it: a + package published before its users build (ADR 0573 §2, ADR 0667 §3). +- §62 **How it is checked.** Whole (mesh-sdk, against the real bus): values of one, ten and a hundred times the per-value + limit stored, sent as a reply and as an event, and read back whole by a caller and a consumer the manifests declare, + and refused to a module that declares neither; a replaced large value leaves its old object listed `key gone` beside + the new one, and a key deleted leaves its object listed `key gone`, each removed by the drop tool and by nothing else; + a sent object is readable within its 7 + days and expired after; each drop without who asked is refused. Tests 69 and 90. + +### The pipeline makes the check, and the checker goes + +- §63 **Every step runs on parts built from the change.** Every pipeline step that runs a change's code runs on parts built + from the change's own heads, in the build seat's sandbox, with no network and no bus account (ADR 0666 §17, ADR 0448 + §2, §4): the composition step composes each machine's declaration with the composer built from the heads and refuses + a change in which two modules own one path; the build seats' steps run lint and tests. A step whose serving part the + change alters runs from the change's build of it, built within 20 minutes or ending `waiting for a build node` or + `did not build`, the first tier of *The check on the change's own acting parts* made the runner's. +- §64 **The run on request.** The runner's verb `run` takes one or more members, each a module at a named commit, runs them + through their pipelines and delivers nothing, replacing the checker's try (ADR 0666 §18). Its callers are the + sessions, as the agents' bus identities the tool runner proves, and the operator, and nothing else, since a run of a + group builds and publishes its packages' tagged versions (design 51). The runner says `run-started`, the indexer builds + the run's candidate from it, and the candidate's composition step writes no want (ADR 0656 §9); the candidate is + dropped on that run's `pipeline-ended`. A step that could not run ends `could not run`, never as passed. A + composition step for a node that sleeps passes it with a warning (ADR 0593 §9). +- §65 **The runner is an acting part in the checker's place.** The acting rule's step `check` becomes the runner's step + `pipeline` (*What ADR 0656 leaves to this design*): its own change runs on its own new version, a failing new runner + holds while the old one runs pipelines (ADR 0656 §4), and the pipelines in flight when its holder is replaced are + taken up again from its kept state (ADR 0666 §19). +- §66 **What the checker did, and where it goes.** + + | The checker's | From G's delivery | + |---|---| + | the merge check, one status `mesh/check` | the pipeline, its result in `pipeline-ended`, the runner's `show` and the delivery record; no status on the forge | + | the try | the run on request | + | the composition check | the composition step, the assigner's | + | the kept verdicts and the carry | the runner's `kept`, which starts empty, and its carry (ADR 0548 §5); the checker's verdicts go with its buckets | + | the checking program: the manifest validator at the heads, the cycle rule, the composer, the rule names | the indexer's validation before take-in; the indexer's cycle rule; the composition step; `widens` in the pipeline's end | + | the `run` ask, published by the checker's account | published by the runner's account and the indexer's (its validation), each with step keys under its own name | + | its buckets and bus account | removed with the gate's forge merge token after the first pipeline after G's delivery has delivered a change end to end, on the operator's one word, the buckets through `drop` after its snapshot, whose restore is tested first (ADR 0667 §12) | + +- §67 **How it is checked.** Whole (runner, assigner): a run on request of `feature/1966-x` delivers nothing, and a call of + `run` from another account is refused; a composition step refuses two modules owning one path; a step whose part + cannot be heard ends `could not run`; a node asleep passes with a warning; a runner stopped mid-step takes its + pipelines up again. Live: once the checker is removed no module claims the seat `mesh-delivery-checker`. Tests 64, + 84 and 88. + +### Who may say what + +- §68 **Callers per verb, in the seat's definition.** Each seat definition names, per verb, the callers allowed to call it, + in its field `callers` (holder roles, modules, the operator, the healers), which the grants part reads as its one + source of caller limits; the runner's verbs are the first (ADR 0666 §20). A verb whose definition names no `callers` + admits only the seat's holder roles and the operator, so a field left out opens nothing. It replaces the holder's + trusted config value `callers` of the seat `git` and the package seats (design 51), which no holder set for most + verbs. A format its running reader cannot read, so its reader is delivered first (ADR 0667 §4). +- §69 **A step's result from its holder only.** The runner takes a step's result only from the bus account of that step's + seat holder, naming the commit and the build-input fingerprint; a result from another account, or naming another + commit or fingerprint, is refused and said (ADR 0666 §21). +- §70 **A review gates, once it can.** A code review's result is taken through the verb `review` from the reviewer's bus + account and refused from the commit's author, so a review gates where a policy says `required` (ADR 0666 §22, which + changes ADR 0659 §2). Until a record binds each forge account to its bus identity no policy may say it (*A module's + registration*; issue 1966, note 157). +- §71 **A widening is said, never refused.** A change that alters any part serving a step (the runner, step detection, the + lint rule, a build seat's test runner, the composer), or widens an acting rule, a grant or a credential, says so in + its `pipeline-ended`, its field `widens` naming each. The running runner computes it, from the sources of the change + against those of the watched branch, never the runner built from the change, so a change cannot hide its own + widening; nothing refuses it for that (ADR 0666 §23, the operator, 2026-11-10). The teller passes `widens` on to every + channel, as it does `recovery-merged`. This is what replaces ADR 0656 §8's naming in the Approve ask. +- §72 **How it is checked.** Whole (runner, grants part): a forged step result and an author's own review are refused; a + caller not in a verb's `callers`, and any caller but the holder roles and the operator of a verb with no `callers`, + are refused by the bus; a change widening a grant, and one altering the Go build module's test runner, each name it in + `widens`, computed by the running runner, and are delivered. Tests 85 and 86. + +### Health and going back + +- §73 **The runner's events.** Each on `mesh.seat.mesh-pipeline-runner.event....`, + a branch or group name escaped as a key's name is, declared in the seat's definition and published by the runner's + account alone (ADR 0666 §24, ADR 0363 §2): + + | Event | Says | + |---|---| + | `pipeline-started`, `pipeline-ended` | a pipeline's start, and its end with every step's result, its test counts, the source revision and `widens` | + | `group-ended` | a group's one pipeline's end, on which alone its wants are written | + | `run-started` | a run on request's start, with its members at their commits | + | `step-started`, `step-ended` | each step's start and end: the step, its status in the policy, its outcome, its build-input fingerprint, its test counts, for a carried result what it was carried from, and for a rerun that it is one and the caller the runtime proved; a bypassed step too | + | `wait-held`, `wait-ended` | a step's wait: on a person (approval, code review), on a build node, on the revision view | + +- §74 **What a session hears after its merge.** Between its `merge-pull` and the delivery, a session waits with `mesh_wait` + on the runner's `pipeline-started` and `pipeline-ended` of its module, branch and commit, and then on the status + view's `delivered` of the source revision; a failed head reaches it as `pipeline-failed` (*What holds a delivery*). +- §75 **Nothing stands still unseen.** The watchman times each step's owed progress from `wait-held` and `step-started`, + never by run length; a step or a watched branch's head standing still five minutes is its finding `pipeline-stands`, + raised by the condition keeper and cleared on the step's end, or for a head on its `pipeline-started` (ADR 0564 §2, + §4). A step waiting on a person is said as waiting on that person (ADR 0283), never as a stall; a group waiting for + its members, and a merge behind a pending revision, are governed by their own bounds (ADR 0666 §24). At its start the + watchman seeds its open waits from the runner's `stands`, as it seeds `take-in-waits` from `indexer_pending`. +- §76 **The runner's own health.** Its manifest declares its health check, run outside its process (ADR 0240): the runner + answers `show`, and its durable consumers of `step-ended`, `take-in.taken`, `package-published` and `decided` are + each less than five minutes behind; a failed check names which. +- §77 **The runner waits for its view.** With no revision view it says so in `_first_views` and raises `view-waits` + after five minutes, as every part does (ADR 0656 §6). +- §78 **Going back is one act.** A failing new version holds while the old keeps running (ADR 0656 §4); a return to an + earlier build is one value in the placement, the module's `build` in its placement key, set by a placement proposal + (ADR 0666 §24). +- §79 **How it is checked.** Whole (runner, watchman): a step whose worker dies raises `pipeline-stands` within five minutes; + a head with no pipeline raises it and its `pipeline-started` clears it; a step waiting on the operator raises nothing; + a runner restarted mid-step takes its pipelines up again; its health check fails naming a consumer behind. Whole + (assigner): a placement's `build` delivers the earlier build. Tests 76, 77 and 88. + +### A recovery fix is an ordinary merge + +- §80 **No recovery merge verb.** A fix that recovers the mesh is merged like any change on a watched branch, by a + session's `merge-pull`, and runs its pipeline; no trusted recovery merge verb is built (ADR 0666 §26). The shape this + design sketched for one (a verb `merge` of the seat `mesh-delivery` with `recovery-callers`) is dropped. +- §81 **Named a recovery, said everywhere.** Its author names it a recovery by the seat `mesh-delivery`'s verb `recovery` + (the commit and why), never by a pull request's text: the gate marks the delivery record `recovery` with the caller + the runtime proves, and says `recovery-merged`, which the teller passes on to every channel (ADR 0666 §26). A widening + it carries is said in its `pipeline-ended` like any other (*Who may say what*). +- §82 **How it is checked.** Whole (gate): a merge named a recovery is marked in its record and `recovery-merged` is said; + a recovery named by a description is not. Test 92. + +### The tools + +§83 Every new state has a tool that reads it and every new act a tool that starts it by hand (ADR 0666 §25), against the +four lines a module's tools answer; every read tool of a store also answers its fill against its cap: + +| Part (seat) | Read everything it holds | Do by hand what it does itself | Recover without help | +|---|---|---|---| +| the runner (`mesh-pipeline-runner`) | `show` (one pipeline whole, its steps, step log and outputs), `list` (the pipelines of a module, a branch or a commit), `stands` (what stands still, since when), `why-carried` (where a step's result was carried from), `kept` (the kept results by digest) | `start` (a delivering pipeline for a watched branch's head), `run` (the run on request), `rerun` (one step again) | `accept` (a failed `optional` step, with why), `cancel` (a pipeline, dropping its asks), `ask-again` (a declined `approval`) | +| the indexer (`mesh-catalog`) | `registrations` (with each one's history); `indexer_edges` (an artifact's edges per watched branch), `indexer_order` (the order of a change, its cycles named), `indexer_catalog_difference` (two revisions); `indexer_pending` (a group with its missing members) | `register`, `policy`, `watch`, `unwatch` (each on the operator's Approve) | `indexer_seed_again` (the first registrations' ask raised again), `indexer_take_in_pending` (a group taken in as gathered), `indexer_drop_pending` (dropped) | +| the gate (`mesh-delivery`) | `source` with a branch (`gate_source`: every branch's source revision) | — | `gate_source_again` (say `source-moved` again for a branch) | +| the forge module (`git`) | `git_failed_steps` (the failed `required` steps it holds, by module and fingerprint, with the run that failed each) | — | `git_failed_steps_drop` (drop one, with why) | +| the grants part (`mesh-grants`) | `grants_callers` (the callers each verb admits, read from the seat definitions) | — | `grants_why_refused` (for an account and a verb, the grant it lacks and the definition's line that leaves it out) | +| the build seats | `steps` (the steps detected for an artifact) | `steps` with `again` (detect again) | `drop` (ADR 0664 §4) | +| the builder (`mesh-builds`) | `builder_versions` (a package's published versions) | `builder_publish_again` | — | +| the assigner (`mesh-wants`) | `assigner_why_not` (why a module is not on a machine yet: the pipeline it waits for, its hold, the placement, the node's last `running`) | — | `assigner_release` | +| the watchman | `watchman_views` (how far behind each part's view is, from each part's `view-waits` and first-view tools) | — | each part's `_first_view_again` | +| the operator-channel | `asks` (with each asker's asks held back) | — | `ask-again` (offer a held-back ask now) | +| every module that imports mesh-sdk's small object store package | `_state_objects`, `_sent_objects` | — | `_state_objects_drop`, `_sent_objects_drop` | + +§84 Every recover tool writes its act, who and why in the hand-act log of the seat `mesh-hand-acts`. How it is checked: +each tool answers its whole-module test's case, every store's read answers its fill, and `mesh_describe` reads each +(test 93). + +### The runner's kept data, and the build capacity + +§85 Each kept form names its section or template, its retention and its cap; a state template fixes its own size (ADR 0595 +§3), up to 64 MiB. The numbers are estimates, to be read from the fill tools once G runs: an estimated 20 merges a day +(as *The delivery record*), each starting a pipeline for about 3 modules of its repository whose fingerprint it changes, +and an estimated 2 mesh-sdk publishes a day, each rebuilding about 120 artifacts and starting about 120 pipelines: about +300 pipelines a day, of about 6 steps each. + +| Part | Kept data | Section or template | Retention | Cap and growth | +|---|---|---|---|---| +| the runner | `pipelines`: one key per pipeline, keyed by watched branch, source revision and module, its steps, statuses, outcomes, fingerprints and test counts | old `state` section | a pipeline a delivery record cites, deleted with that record (90 days); a superseded pipeline, a run on request's and one no record cites, 7 days after its end; always while it is its branch's newest for its module | 256 MiB; about 300 pipelines a day of about 4 KiB is at most about 105 MiB at 90 days, under half the cap | +| the runner | `step-log`: one entry per step's start and end, its output by name | old `streams` section | by its age, 90 days, as long as the delivery records that cite its steps | 128 MiB; about 3,600 entries a day of about 0.2 KiB is about 63 MiB at 90 days | +| the runner | `step-outputs`: a lint or test step's output | old `objects` section | 7 days | 1 GiB; about 1,200 steps run a day (the rest carried) of about 50 KiB is about 420 MiB at 7 days | +| the runner | `kept`: a package's passing result per level, by digest | old `state` section | 30 days unread | 128 MiB; a publish changes the digest of about 1,000 importing packages at two levels, so about 4,000 entries a day of about 0.3 KiB, about 36 MiB at 30 days | +| the indexer | `registrations` and its stream `registration-history` | old `state` and `streams` sections | the life of the mesh, never derived again | 64 MiB (*A module's registration*) | +| the indexer | catalog graph revisions, per watched branch | objects in the old form (ADR 0619 §2) | while a record pins it or it is its branch's current | 512 MiB, as *Two graphs, one source of truth*; a second watched branch on a few modules adds a few MiB | +| the gate | `source`: one key per watched branch | its old `state` section, as today | 64 revisions of each key; a branch's key deleted when `registration-changed` says the last registration watching it unwatched it (`unwatch`) | a few KiB | +| the forge module | `failed-steps`: failed `required` steps by module and fingerprint, with the commits each was seen at | state | an entry deleted once none of its commits is a watched branch's head or the head an open pull request names, judged on each `branch-moved` and each pull request's close | the template's 64 MiB; an estimated 50 failed steps a day of about 0.5 KiB, most gone within days, is under 1 MiB | +| every module that imports the object store package | `-objects` beside each state | objects | an object no key names, its key deleted or its value replaced, until `_state_objects_drop` on a person's word (ADR 0595 §4, ADR 0666 §16) | the template's; nothing reserved until written | +| every sender of the new form, and a foundation part by its old `objects` section | `_sent-objects` (`sent-objects`) | objects, planned by the bus module with no manifest field | 7 days, transport (*Any size*) | the objects template's cap; full is the bus module's `full` | +| the grants part, for each seat event a definition keeps | `SEAT__`, among them `SEAT_mesh-pipeline-runner_*`, `SEAT__step-ended`, `SEAT_git_branch-moved`, `SEAT_mesh-builds_package-published` and `SEAT_mesh-catalog_registration-changed` | a stream the grants part makes with its durable consumers | the seat definition's age (`keep`), 90 days unless the definition names less | the seat definition's cap (`keep`) | + +§86 **The build capacity.** A pipeline runs a module's steps once per commit on a watched branch, carrying what did not +change, where the check ran before the merge and carried after each trunk move; a group runs one pipeline per watched +branch; a second watched branch runs the steps of the modules it carries once more. A mesh-sdk publish asks about 120 +lint steps and the test steps of the importing packages, about 240 more, the rest carried; a newer publish of the same +package supersedes the older one's rebuild pipelines, so two publishes close together run the rebuilds once. The first +pipelines after G's delivery run every module's lint and tests once, since `kept` starts empty (ADR 0666 §21): about +120 modules, one round of the build capacity, taken in the `check` stream's order; from then they run only what +changed. The steps go on the build seats' stream `check`, +after `delivery` and before `recheck`, within each node's build capacity (ADR 0525 §4, §5), and a run on request on the +same stream. How it is checked: live, the build nodes' time per day in the week after G, read from the build modules' +step keys and the builder's build log, against the checker's last week (test 99). + +### How milestone G itself is checked and delivered + +- §87 **Under the clauses as they stand.** Milestone G is checked and delivered under ADR 0656 §5 and the clauses it changed + as they hold before its delivery: the acting parts (the indexer, the build modules, the assigner, the grants part, + the node-engine, and the checker, which makes G's own check) act from the milestone's heads, each first in its step, + in the order of their ordering edges; the trunk-only want, the checker and the Approve per merge as ADR 0661 leaves it + govern this delivery, no exemption is asked and no delivery goes ahead of it (ADR 0667 §2, ADR 0644 §1). +- §88 **Its other parts as any module.** mesh-sdk's small object store package is a package published before the modules + that use it build; the manifest validator moves with the indexer; the runner is placed like any module and acts only + from the next change on; mesh-delivery's `contracts` and `sdk` move under its `modules/` folder (ADR 0667 §3). +- §89 **Readers first** (ADR 0667 §4): an edge of kind 4 each, which the delivery plan derives: the indexer's manifest + validator for the seat definitions' new fields (`callers`, `acts`, `subject`, `keep`) before any definition using + them; on each node the node-engine that reads `form` and `catch-up` before any manifest writing them. +- §90 **The stream maker first** (ADR 0667 §5): the grants part, which makes each kept seat stream and its durable + consumers, before the parts that hear a kept stream. +- §91 **The bus module first** (ADR 0667 §6): the bus module, with `-objects`, the sent store and the grant of each + state's object store to its owner alone, before the modules requiring them, the grants part writing that grant from + the milestone's heads. +- §92 **A bus restart is said first** (ADR 0667 §7): a restart of the bus its new version needs is said to the operator + before it starts. +- §93 **A gap is built, not worked around** (ADR 0667 §8): an order the delivery plan cannot derive from the edges is + built in the delivery system within G, never a step done by hand. +- §94 **A node-engine of the version before applies what it can read** (ADR 0667 §9): it applies every want whose + manifests use no field it does not read, and a want that uses one waits, by the reader edge above, for the new + node-engine on that node. +- §95 **The new assigner and the new merge switch on the first registrations** (ADR 0667 §10). The new assigner, which + writes a want only from a passed pipeline, is delivered before the forge module's new `merge-pull`, which asks no + Approve, and before the runner: an edge of kind 4 from `mesh-delivery-assigner` to each, declared in their manifests' + `needs` since nothing else says it (the design's choice: without it a merge made with no Approve could reach the old + assigner, which writes wants from the trunk with no pipeline). Both switch on one fact, the first registrations + written, said as `registration-changed`: until it the new assigner keeps the trunk-only want and the new + `merge-pull` refuses every merge, naming the missing registrations. +- §96 **Until then, the operator merges a fix at the forge** (ADR 0667 §11). Until that fact an ordinary change waits for + it, and a fix, an indexer fix included, is merged by the operator's own hand at the forge (ADR 0661 §3) and + delivered under the trunk-only want still in force; `indexer_seed_again` asks the first registrations again. Before + the milestone's delivery every merge goes as ADR 0661 has it. +- §97 **What is removed, and when** (ADR 0667 §12). The checker's buckets and bus account and the gate's forge merge token + are removed only after the first pipeline after G's delivery has delivered a change end to end (test 63), the runner + then making every check in the checker's place (ADR 0666 §19), and only on the operator's word, asked once for all + three. The checker then leaves the manifest validator's list of foundation parts (ADR 0666 §4) and is no longer + placed, so no running module declares its buckets and they + are orphans `orphans` lists; they go through ADR 0595's `drop` on that word, after the stream snapshot `drop` takes + (ADR 0595 §4, §5, §10), kept 90 days as the delivery records are; a restore from that snapshot is tested before the + word is asked. The runner is on that list from G's delivery. +- §98 **Told to the operator before it starts**: G's own merges are asked as ADR 0661 has them (ADR 0667 §2, §11); the one + ask of the seed registrations; the bus's restart (ADR 0667 §7); and later the one word for the checker's removal + (ADR 0667 §12). +- §99 **How it is checked.** Tests 94 to 97, and 100 for the assigner's edge. + +### Choices this design makes under ADR 0666 and ADR 0667 + +| Choice | Why | +|---|---| +| §100 The registrations in the indexer's old `state` section with their history on an old stream, capped at 64 MiB, kept for the life of the mesh | the indexer is a foundation part (ADR 0619 §1 to §3, ADR 0666 §4); a registration keeps every version with who and on whose Approve, and nothing derives it again | +| §101 The runner a foundation part, its kept data in old sections, on the manifest validator's list | the bus module's own delivery waits on its pipeline (ADR 0666 §4) | +| §102 The want's compare-and-set keyed per module and watched branch | source revisions are kept per watched branch, so two branches' revisions have no order; comparing across them would let `main` refuse `beta`'s want (ADR 0666 §9) | +| §103 A second start of a pipeline for the same source revision and module, on `take-in.taken` and `package-published`, writes nothing, by the pipeline key's compare-and-set | the runner starts on both events (ADR 0666 §13, §15); the key it already keeps judges a start in one place, so neither producer knows of the other | +| §104 A refused new indexer revision holds later merges on its branch until a revert lands | the validation runs after the merge and before take-in (ADR 0666 §6), so the refused revision has landed; taking later merges in past it would deliver them on a graph its own validator refused | +| §105 Each check at its row's level, every requirement R1 to R18 keeping its named test | the record's rows name the levels; the test budget and one check per release bound what runs | +| §106 The approval asked as the operator-channel's accepted kind `ask` on its work queue | the seat has no verb `ask`: an ask is its accepted kind, by the asker's own name (design 46 §10) | +| §107 The gate no foundation part, its `source` in the old section it already has | the bus module's delivery waits on no act of the gate's; only the key's layout changes | +| §108 The verbs `register`, `policy`, `watch`, `unwatch`, `registrations` and the event `registration-changed` on `mesh-catalog` | one verb per trusted act, each its own ask naming what moves; readers follow the event (ADR 0596) | +| §109 The seed of the first registrations proposed as one ask, with the statuses the check judged until then and approval `bypass`, raised again by `indexer_seed_again` | registering is a trusted act (ADR 0666 §3), and one ask for the whole seed is how placement was adopted (ADR 0567); a declined or expired seed must not leave the pipelines stopped with no way on | +| §110 Code review refused as `required` until forge accounts are bound to bus identities | a review is refused from its author (ADR 0666 §22), which cannot be told until then | +| §111 The seat `git`'s read verb `branch` and event `branch-moved`, kept on its seat stream | registering reads a branch's protection through the seat `git` (ADR 0666 §3); a branch moves by a merge or an admitted push; a stream keeps a move said while the bridge is down | +| §112 The watchman's finding `pipeline-stands` for a step or a head still five minutes, cleared by the step's end or the head's `pipeline-started` | the record's five minutes (ADR 0666 §24), one finding for both, timed from the runner's events | +| §113 The runner's finding `pipeline-failed`, the author's for two hours, then the operator's | ADR 0666 §24 with ADR 0505 §5, §6 | +| §114 A module's watched branch and its `build` in the placement key, set by a proposal | a placement names the branch and a return is one value in it (ADR 0666 §6, §24); every placement change goes by a proposal (ADR 0567) | +| §115 The gate's `source.`, its verb `source` and event `source-moved` | one source revision per watched branch (ADR 0666 §6), read through the seat (G's prepare, the indexer's watch of the gate's `source`) | +| §116 The build seats' verb `steps` and accepted kind `step` on `check`; the assigner's kind `step` on `mesh-wants`; `step-ended` with the node in its subject | steps are asked on the serving seat's work queue (ADR 0666 §7); the `check` stream already orders after `delivery`; a node's account says only its own node's ends (ADR 0363 §2) | +| §117 The outcomes, with `accepted`, `superseded` and `cancelled`; an approval expired or refused ends `declined`, replaced `superseded` | every end a step can have is said as itself (ADR 0666 §10) | +| §118 `approval` asked last, its `about` the module and the branch | the operator approves a commit the mesh already judged, the point ADR 0661 §3 kept in one place; one ask per module and branch, a newer commit's replacing it | +| §119 A superseded pipeline's asks dropped by `drop`, and a pipeline superseded only when its module's fingerprint changed | a cancelled pipeline must leave no ask on a stream (ADR 0664 §4; G's prepare); a module the commit did not change has nothing newer to run (ADR 0666 §9) | +| §120 A group's wants only on `group-ended` | a group is delivered whole or not at all (ADR 0666 §9; research 086, R5) | +| §121 The six kinds derived from existing words, `needs` per artifact only where nothing says it | one form of each fact (ADR 0656 §6); the catalog entry's `needs` already exists from G | +| §122 The cycle rule in the indexer's graph | the graph is the indexer's, and the checking program goes with the checker | +| §123 The builder's seat `mesh-builds` and its event `package-published` | the record has the builder say the publication as an event of its seat (ADR 0666 §15); its waits move only when a later change gives the seat its verbs | +| §124 Each state's object store `-objects` through mesh-sdk's small package, sent objects readable by a standing grant derived from the manifests, and the package's two tools | one object store per state, granted to its owner; the sender's declared callers and consumers read what it sends (ADR 0666 §16); only its users import it, so nothing else is rebuilt | +| §125 The `run` ask published by the runner's account and the indexer's; the verb `run` called by the sessions and the operator only | the steps and the validation run code from the change, in the sandbox only, and each asker reads only its own step keys (ADR 0363 §2); a run publishes tagged packages, so nothing else may ask one | +| §126 The checker's step `check` becomes `pipeline`; `kept` starts empty | the runner is an acting part in the checker's place (ADR 0666 §19); a step's result is taken only from its seat holder (ADR 0666 §21), so no verdict of the checker's is taken in, at the cost of one round of every module's tests | +| §127 The field `callers` in a seat definition replaces the holders' trusted `callers`, a missing field admitting the holder roles and the operator only | the record makes the definition the grants part's one source (ADR 0666 §20); a field left out must open nothing | +| §128 `widens` in `pipeline-ended`, computed by the running runner over every part serving a step, passed on by the teller | a widening is said and nothing refuses it (ADR 0666 §23, §26); a change must not compute its own | +| §129 `merge-pull` judged from the forge module's own `failed-steps`, heard from `step-ended`, any run at the head's fingerprint counting unless a later rerun of that step passed, the protection read again on every `branch-moved` | the record has it judge from the events it consumes, never by a call, so a head merges while the runner is down (ADR 0666 §11, ADR 0596) | +| §130 The new assigner before the new `merge-pull` and the runner, by `needs`, both switching on the first `registration-changed` | a merge with no Approve must never reach an assigner that writes wants without a pipeline (ADR 0667 §10) | +| §131 The forge bridge's finding `branch-unprotected` | a move of a branch whose protection was removed starts no delivering pipeline and is said (ADR 0666 §6) | +| §132 The verb `recovery` of `mesh-delivery` | a recovery is named by its author as a fact the bus keeps with who said it, never read from text (ADR 0666 §26, research 086 R10) | +| §133 No forge status for a pipeline | the step results live on the bus (research 086 R10), shown by the runner and the delivery record; a status would be a second copy outside it | +| §134 The runner's `pipelines` kept as long as the delivery record that cites each (90 days), a superseded, run-on-request or uncited one 7 days, its step log 90 days by age, its outputs 7, keyed by source revision | a pipeline is read back as long as the record that delivered it; one nothing delivered has no trace to serve after a week; about 300 pipelines a day must fit its caps, so they are 256 MiB and 128 MiB | +| §135 The forge module's `failed-steps` kept until no head it was seen at is a watched branch's head or an open pull request's | an entry judges only a head that can still be merged; once neither names it, nothing can be refused by it | +| §136 The gate's `source.` deleted when the branch's last watch goes | a branch no registration watches makes no revision, so its key would name nothing | +| §137 The sent store kept 7 days by age, with no person's word | an object sent carries one reply or event and is transport, not kept data (ADR 0595 §4); its readers fetch it within a call's bound or a consumer's lag | +| §138 A replaced object listed `key gone`, as a deleted key's is | no key names it, and one listing lets the drop tool take both on a person's word, as ADR 0595 §4 and ADR 0666 §16 have it | +| §139 The checker's removal after the first end-to-end pipeline, on one word, its buckets through `drop`, the snapshot kept 90 days | ADR 0667 §12 asks one word for all three; 90 days is the delivery records' retention, the longest a trace reads back | +| §140 The pipeline steps on the `check` stream after `delivery` | a delivery's build is never held by a pipeline, as a check never held one (ADR 0525 §4) | + +## The delivery record, and what the gate owes the forge + +### The delivery record + +*ADR 0656; ADR 0595, ADR 0596, ADR 0597, ADR 0603 §7, ADR 0493 §3, §11, ADR 0633 §4, §5, ADR 0441 §1, ADR 0548 §8, +ADR 0564 §2, ADR 0363 §2.* + +§141 From research 085's milestone the gate, holder of the seat `mesh-delivery`, keeps one **delivery record** per +milestone's delivery, a ledger instance (ADR 0595's template `ledger`, ADR 0597) it alone writes (ADR 0656). It +holds: + +- §142 **the group's state**: the heads, the verdicts, the checks and the asks, which `merges` holds today; from milestone + G's delivery the pipelines' `step-started`, `step-ended` and `pipeline-ended` in place of the verdicts, the checks and + the asks, kept as long as the record (ADR 0666 §9); +- §143 **the trace**: the milestone, its issues, its pull requests, their commits, and the graph revisions the delivery + moved between; +- §144 **what happened to each entry on each node**: for every entry of the graph difference, taken in, built, checked, + composed, wanted, running healthy, held (with the hold's reason, so a reader of the record sees why an entry stands + still without asking its holder), failed (with what failed), or away (ADR 0656). + +§145 It holds no tiers, and only the gate orders work by it, so it is no second copy of the keys, the objection research +063 had to a stored plan (research 085, Findings 10). Its fields, retention and tools are this design's choices under +ADR 0656. + +- §146 **The outcomes arrive as events, through a durable consumer of the gate's own.** The gate's manifest `consumes` + each step event of *What each step says* (below), and `wait-held` and `wait-ended` of each holder. The consumer + resumes from the last event applied; an event applied twice changes nothing; an event is applied only when it comes + from its owner's account; and running healthy is taken from the node-engine seat's `running` event, never from a + have read across the seam (ADR 0596). +- §147 **It ends when every present node runs its entries**, and the gate says `record-ended` on the seat `mesh-delivery` + with the record's milestone and its nodes away. It names the nodes away, each with since when, and ends without + waiting for them, as a node away holds nothing (ADR 0603 §7). +- §148 **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. 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. +- §149 **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. +- §150 **An Approve given holds at its heads, until G's delivery.** An Approve the operator gave for a group's heads holds while those heads + stand: a check of the same heads asked again, after a main move or for any other reason, records its verdict in + the record and asks the operator nothing again (issue 1972). The Approve is withdrawn only as ADR 0441 §1 has it, + when a member stops being mergeable, and a carried verdict is read as current (ADR 0548 §8). From G's delivery no + Approve is asked per merge; an `approval` step's answer is the runner's, kept in the record as its step's end + (ADR 0666 §10). +- §151 **From G's delivery, a record for every landed merge.** A session merges through the seat `git` and the gate merges + nothing, so every merge is one the gate did not make: the gate opens a record for each source revision of a watched + branch the indexer takes in, a group's members in one, and `forge-made` no longer marks a record apart. A merge its + author names a recovery is marked `recovery` (*A recovery fix is an ordinary merge*). +- §152 **A small fix has a record of its own.** A small fix is a milestone of one issue (ADR 0493 §3), so it has a + delivery record like any milestone; nothing is merged outside a released milestone (ADR 0493 §11). +- §153 **It pins the graph revisions it points at.** The gate says, as the seat `mesh-delivery`'s events `pinned` and + `freed`, which catalog and assignment graph revisions a record starts and stops pointing at, and as + `heads-replaced` that a candidate's heads were replaced or its pull request closed. Only the gate's account may + publish them (ADR 0363 §2). The indexer and the assigner hear them through a durable consumer each, declared in + their manifests' `consumes`, which resumes from the last event applied after a restart, so a `freed` said while one + of them was down is still applied; neither reads the gate's state (ADR 0596; *Two graphs, one source of truth*, + below). +- §154 **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. +- §155 **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. +- §156 **The switch carries today's state in, once.** At the new gate's start, `merges` and every group in flight become + records, and from then on the gate writes `merges` no more; `turns`, which nothing writes while the check gates no + merge, is dropped with the hold (*Nothing merges into a repository under check*). 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 delivering milestone G again after a revert makes no second record and asks + nothing again. A revert of the gate is the delivery system's restore of its kept data (*A module's kept data on the + bus, backed up and restored with its delivery*). +- §157 **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`. +- §158 **What the gate owes the forge is said, not watched, and a merge made at the forge has a record too** (*What the + gate owes the forge*, below). +- §159 **Its tools**, against the four lines a module's tools answer: + + | Line | The gate | + |---|---| + | read everything it holds | `gate_record` (one record whole: the group's state, the trace, each entry's outcome per node, the nodes away), `gate_records` (every record with its fill against the value limit, and the ledger's fill against its cap), `gate_consumer` (its durable consumer: the last event applied per family, the events waiting, the age of the oldest) | + | do by hand what it does itself | `gate_record_repair` (for one record, asks each owner for the outcome of each entry missing one through that owner's read verb: the indexer's `take-in` read, the builder's build read, the checker seat's check read (from G's delivery the runner's `show`), the seat `mesh-wants`'s `entry`, and the node-engine seat's verb `composition` of that node; applies the answers as the consumer would) | + | recover without help | `gate_record_end` (end a record left open, a node gone for good or an entry that never runs, with the reason, recorded in the hand-act log of the seat `mesh-hand-acts`) | + | one seat, many tools | the seat `mesh-delivery`'s verbs, with `groups` answering a group's members and the acts said for it | + +- §160 **How it is checked.** In the gate's whole-module test: the gate stopped and restarted in the middle of a delivery + leaves the record complete with no outcome counted twice; the events `pinned`, `freed`, `heads-replaced` and + `record-ended` are said; a `freed` from an account other than the gate's is refused by the bus; a group approved + before the switch is not asked again after it, nor after milestone G is delivered again following a revert; a record ends once every present node runs its + entries, naming the nodes away; a held and a failed entry are kept with their reason; a record open past 24 hours + raises `record-open`, cleared by `record-ended`; a node away that wakes adds its outcomes and is no longer shown + away; a group's heads approved and checked again ask the operator nothing again; a small fix's merge gets a record + of its own; a record past its retention is removed and frees its revisions; the tools read a record, list the + records with their fill, and a repair completes a record missing an outcome through the owners' read verbs. In the + forge bridge's whole-module test, a group's members are read from `groups` and its end published again from it, and + a read of `merges` by its account is refused. Live: one call of `gate_record` traces an issue of the registry + milestone to each node's running build, also after six later merges; a node away is named with since when and its + outcome taken on waking; a record of 30 modules on four nodes stays under 256 KiB; a group checked again at old + heads after a main move is not asked of the operator again (issue 1972). + +### What the gate owes the forge + +*ADR 0596 §1, §2, ADR 0656 §7, ADR 0437 §8, ADR 0441 §5, ADR 0633 §1, §4, ADR 0363 §2, ADR 0564 §2; from research 085's +milestone, this design's choices under ADR 0596 and ADR 0656 §7.* + +§161 The forge bridge today performs what the gate's states `merges` and `owed` ask (a merge, a status, the delivery's +view, a comment, a line on the commit's note) by watching those keys (ADR 0437 §8), and the forge module writes the +checker's `mesh/check` from a watch of the checker's state `owed`: reads of another module's bus state that ADR 0596 +§2 ends when their owner moves. From research 085's milestone: + +- §162 **Every act is an event, under one id.** The gate says each act it owes as the event `owed` of the seat + `mesh-delivery`, on `mesh.seat.mesh-delivery.event.owed..`, carrying the act, the pull request, + the head and an id; the checker says its status the same way, as the event `owed` of the seat + `mesh-delivery-checker` on `mesh.seat.mesh-delivery-checker.event.owed..`. Only the owner's + account publishes each (ADR 0363 §2). The subject per repository and pull request lets the bridge, a person and a + test read one pull request's acts alone. The seat events are kept on each owner's stream `owed`, 7 days and at most + 64 MiB (an estimated 20 deliveries a day, with about 15 acts of about 1 KiB each, is about 2 MiB over 7 days). +- §163 **Every act is safe to repeat at the forge.** A status and the delivery's view replace what stands; a merge of a + pull request already merged at that head is answered performed; a comment and a line of the note carry the act's + id, and the seat `git`'s verb skips an id the pull request or note already holds. This is chosen over recording the + id before the act: an id recorded and then not performed (the bridge stopped between the two) would be lost, while + an act repeated changes nothing. The bridge's own state `performed`, a key-value state in the new form (ADR 0595), + form 1, one key per id with the outcome and when, kept 7 days as the stream is, its size the one its template fixes + (ADR 0595 §3), is therefore a + saving and a read, never what keeps an act from happening twice. +- §164 **Every outcome is said, and the gate records it.** The bridge says each act's outcome on its own stream `forge` as + the event `owed.ended`, with the id and one of `performed`, `refused` (the forge's own words), `could not be + performed` (the forge did not answer within the call's bound, with the error) or `undecodable` (an act it cannot + read, with why): an act it cannot decode is said, never only logged. The gate hears the stream `forge` through its + durable consumer, writes the outcome into the delivery record, and says an act's id is answered only once an + outcome is recorded. The checker does the same for its own acts: it hears `owed.ended` for its own ids, records each + outcome in its own state (it watches only its own state), says again at its start, under the same id, every act of + its own still unanswered, and lists one unanswered past 15 minutes in its own stalled read. +- §165 **What is unanswered is said again, and stalls by a bound.** At its start the gate says again, under the same id, + every act its records hold unanswered. An act unanswered 15 minutes after it was said is listed by `stalled`, which + the condition keeper raises as the stalled line (ADR 0421 §7, ADR 0564 §2): fifteen minutes is many times a + forge call's time, and short beside the check's hour. +- §166 **A group's merges follow one another, until G's delivery.** The gate says a group's next `merge-pull` only once the previous member's + merged message is heard (ADR 0441 §5), so a member the forge refuses holds the rest, and none is merged ahead. +- §167 **From G's delivery the gate owes no merge, and the checker nothing.** A session calls `merge-pull` itself (ADR 0666 + §11), and a group's members land one by one, the group ending when every member has (*Groups*); the checker's `owed` + goes with the checker, and the runner owes the forge nothing, a pipeline's result living on the bus. The gate still + owes the delivery's view, a comment and the note's line, performed by the forge bridge as above. +- §168 **The bridge catches up from the seat.** At its start the bridge resumes its durable consumer and also reads + `groups`, which answers, per group, the acts the gate said with their ids and their recorded outcomes; an id said + and not answered is performed. Its tools: `bridge_owed` (the ids it performed with their outcomes, and its + consumer's backlog: the last event applied, the events waiting, the age of the oldest), `bridge_owed_again` + (perform one id again now) and `bridge_owed_drop` (drop one id with its reason, written in the hand-act log of the + seat `mesh-hand-acts`, and said as `owed.ended` with the outcome `dropped`). +- §169 **The switch carries what is owed.** At the new gate's start (*The switch carries today's state in, once*) every key + of `owed` and `merges` the old bridge has not performed becomes an act of its record, with an id made of the key and + its revision; the new bridge counts as performed every id whose key and revision the old bridge's own bookkeeping + names, so nothing is done twice and nothing dropped. A merge handed to the forge during the switch is such an act: + its merged message finds the record holding it, so it opens no `forge-made` record. +- §170 **A merge made at the forge has a record too.** A merge the forge reports for which no record holds a `merge-pull` + of that pull request at that head (a person merged it in the forge's own page, as on 2026-11-07 and 11-09) is taken + in like any other merge (*Taken in by the indexer it names*), and the gate opens a delivery record for it, marked + `forge-made`, with no Approve, so its trace and its outcomes on the nodes are kept like every delivery's and no + later merge waits behind a merge the gate never heard. The gate says it as the delivery's `transition` with the why + `forge-made`, which the teller passes on to the operator (the operator, 1921's note of 2026-11-07: "a merge made in + the forge is taken in like any other"). +- §171 **How it is checked.** In the forge bridge's whole-module test: an `owed` said while the bridge is stopped is + performed once after its restart; the bridge stopped between an act and its `owed.ended` performs it again on its + restart and the forge holds it once (the comment and the note by their id); an act said twice is performed once; an + undecodable act is said `undecodable`; `bridge_owed`, `bridge_owed_again` and `bridge_owed_drop` answer, the drop + writing the hand-act log; its account cannot read the gate's or the checker's state. In the gate's: an act left + unanswered is said again at the gate's start under its id and listed by `stalled` after 15 minutes; a group's second + `merge-pull` is said only after the first member's merged message; the switch with a merge handed and not yet + merged makes no `forge-made` record; a merged message with no record's merge opens a `forge-made` record, said as + `transition` with why `forge-made`, and a later merge in the same repository is taken in behind it. Acceptance + tests 53 and 51. + +## The state table, from milestone G's delivery + +§172 **From milestone G's delivery the gate keeps no merge states.** With no check before a merge and no Approve per merge +(ADR 0666 §10, §17), nothing is `proposed`, `ready`, `rejected` or `held` before a merge: a commit's states are its +pipeline's steps, kept by the runner (*Pipeline steps and the policy*). The table, the gate's `table`, `recheck` and +`close` and the one key per pull request go with the check; `stalled` lists the acts owed to the forge past their 15 +minutes and the records `record-open` names. + +## 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 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.* + +§173 From research 085's milestone. One graph answered two questions, which modules exist and which version runs where, +and one module running as several placed versions would have given one entry two shapes and one key two writers; two +graphs answer them, the second computed from the first, so there is one source of truth (ADR 0656; research 085, +Findings 11). How each is kept, read and retained is this design's choice under ADR 0656. + +``` + manifests ──► indexer (mesh-catalog) ──► catalog graph revision ──┐ + ├──► assigner (mesh-wants) ──► assignment graph revision + placements (mesh-module-assigner) ───────────────────────────────┤ │ │ + node capabilities (node-engine seat) ────────────────────────────┘ │ └─► wants, from a trunk revision only + ▼ + a change's assignment graph − the current one = its graph difference = its delivery +``` + +- §174 **The catalog graph is the indexer's.** The indexer, holder of the seat `mesh-catalog`, keeps the modules as their + manifests declare them; it is today's module graph, and *module graph* stays its other name (ADR 0656). At each + revision it holds one entry per module: its bases, its packages, the toolchain version and recipe version of the + build module that builds it as ADR 0603 §2 has them, its provides and requires, its invokes and serves, and its + fingerprint, with the rules the seats declare (*An acting part's new version acts first*, below). +- §175 **The assignment graph is the assigner's, computed and never written by hand.** The assigner, holder of the seats + `mesh-module-assigner` and `mesh-wants`, computes the module versions placed on nodes, with the edges between + versions, from the catalog graph, the placements and the nodes' capabilities, and nothing else writes it + (ADR 0656). Each entry is one module version on a node; its edges are provisioning, contribution, and the acting + parts' versions that are to act on it (ADR 0656), whose rules and versions come in with the catalog graph. It is + kept, but it is no stored plan: it has one writer, no part orders work by a stored plan, and any revision is computed + again from the inputs it names and compared with the one kept, by a tool (below), so a kept revision is a checked + derivation, never a second source of the kind research 063 removed (research 085, Findings 5). +- §176 **Each graph is kept per revision, and each revision names its inputs** (ADR 0656). The indexer keeps a catalog + graph revision for each source revision it takes in; the assigner keeps an assignment graph revision for each change + of its inputs, naming the catalog graph revision, the placements and the capability readings it was computed from. + Both parts are the foundation (ADR 0619 §1), so both keep their revisions as objects declared in an `objects` + section in the old form, each with a cap it declares there, never in the new form (ADR 0619 §2), and neither waits + on the bus module to start; the manifest validator refuses either manifest that declares them in the new form + (ADR 0619 §3). An assignment graph revision is announced by an event of the seat `mesh-wants` and served by its + verbs. This replaces ADR 0603 §2's one key per module with five past values as the graph's form (ADR 0656, which + changes ADR 0603 §2); the keys are written no more, and a revert of milestone G restores them from their backup + (*Reverted*, below). +- §177 **A revision is made by a merge, and also with no merge.** The indexer makes a catalog graph revision when it + takes a source revision in. The assigner makes an assignment graph revision on each new catalog graph revision, on + the seat `mesh-module-assigner`'s event `assignments-changed` (ADR 0567 §5, ADR 0641), and on the node-engine seat's event + `capabilities-changed`, which a node-engine says when the capabilities it states in its node record change + (ADR 0614 §4), so the assigner follows an event and never reads a node record across the seam (ADR 0596). A + placement change is traced like a merge. At its first start with no revision kept, the indexer takes in the head the + gate's source names for every repository by its catch-up path and keeps that as revision 1, and its verb `revision` + answers that it waits until then (lead-47's ruling in G's prepare: a first start has no merge to make a revision). +- §178 **A revision is kept while a record pins it or it is current.** A revision of either graph, and the input revisions + it names, is removed when no delivery record pins it and it is not current: neither trunk's newest revision nor the + newest of an open candidate. Pins are counted per revision, so a revision two records point at stays until both + 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. +- §179 **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. +- §180 **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. +- §181 **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. 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. +- §182 **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). +- §183 **The want stays the one record a node reads.** The assigner writes `want..` from a trunk + revision's assignment graph only; a node-engine reads its want, never the assignment graph, and the have stays what + it runs (ADR 0656, which changes ADR 0571 §8). +- §184 **From G's delivery, per watched branch and between artifacts.** The catalog graph is kept once per watched branch, + each revision naming its branch, and its edges run between artifact references, of the six kinds of *Dependencies + between artifacts* (ADR 0666 §6, §14). The assigner computes a node's entries from the catalog graph of the branch its + placement names, and writes a want from a revision of that branch whose `required` steps passed, in place of a trunk + revision's alone (ADR 0666 §9, which changes ADR 0656 §2). +- §185 **Reverted.** Only the new form is written: the indexer, the assigner and the gate keep nothing of ADR 0603 §2's + keys or of `merges` and `turns` beside the new forms, since nothing reads them after milestone G. A revert of + milestone G is a change like any other, checked and delivered, and the delivery system restores what it must + (*A module's kept data on the bus, backed up and restored with its delivery*, under *A backup before the apply*). + Milestone G changes the form of these kept data, and raises their `form` (a kept data declaring none reads as form + 0, so G's own move takes their restore points): + + | 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`; the states `holds`, `engine-reads` and `hold-bounds` of its mesh-wide hold | catalog graph revisions as objects, form 1; `modules` no longer declared, nor its tool `indexer_read_modules` or its view; the mesh-wide hold, its three states and its tools `indexer_holds` and `indexer_engine_reads` gone, the wait per node being the assigner's (*Composing waits per node, by the assigner*); every reader of it reading the catalog half (*Every other reader of which modules exist*, under *The revision view*, below) | yes: `modules` and the hold's three states as they 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 | + + The wants keep their form: their keys and values are unchanged, only what the assigner computes them from changes, + so G raises no `form` on them and a revert restores none. Restoring the wants would send nodes back to older + 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. 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 + +- §186 **The difference names everything a plan named.** For a change (a pull request's heads, a delivery group's + heads, or a merge), the indexer gives its catalog graph and the assigner its assignment graph. Compared with the + current revision, the difference names the new versions, the moved edges, the touched nodes and the order, read off + the edges, and no plan is stored (ADR 0656). The assigner serves it by the seat `mesh-wants`'s verb `difference`, + for two revisions of the assignment graph or for a candidate against trunk. ADR 0603 §6's tiers, as ADR 0633 §5 + changes it, are a view worked out from that difference (ADR 0656, which changes ADR 0603 §6), read through + `difference`, with the waits and holds read through their owners' tools and the pending revisions through + `indexer_pending`. +- §187 **The preview is a candidate's difference.** Issue 1874's plan preview of a group before its merge is its + candidate's difference, read by one call of `difference`, and needs no code of its own: a candidate's graphs are + computed for its check anyway, so the preview costs nothing more. +- §188 **A changed fingerprint with no difference is refused by name.** When a module's fingerprint in the change + differs from the running one while the difference is empty, the assigner refuses it, naming the module, and the + check fails naming it. +- §189 **A candidate's graphs are kept apart, and dropped with its heads.** A candidate's two graphs are computed per + check, kept apart from trunk's, and dropped on the gate's event `heads-replaced`, said when the candidate's heads + were replaced or its pull request closed. A candidate costs a computed graph per check. +- §190 **Only a trunk revision writes wants**, whether a merge taken in or a placement or capability change made it; a + candidate's graph writes none (ADR 0656). From G's delivery a want comes from a revision of the watched branch a + placement chose whose `required` steps passed, and a candidate is a run on request's: the indexer builds it on the + runner's `run-started` and drops it on that run's `pipeline-ended`, the gate's `heads-replaced` no longer making + candidates (ADR 0666 §9, §18). +- §191 **Each part acts on its own entries.** The builder, the build modules and the grants part each act when their own + entries in the new revision differ from what they run (ADR 0656), reading them through the seat `mesh-wants`'s verbs + and events, never its bus state (ADR 0596), and naming no other module (ADR 0656). The node-engine acts on its want + alone, as it does today: the want is the one record a node reads, and handing every node the assignment half would + have each node read every other node's placements. +- §192 **A node asleep catches up when it wakes.** A node away holds nothing and catches up by its wants (ADR 0603 §7); + a want's new form reaches only a node-engine that says it reads it (ADR 0614 §4, ADR 0618 §2). +- §193 **No delivery goes ahead of a change.** A change is delivered as its own difference, its acting parts' new versions + first in their own steps (ADR 0656); the one exception is the delivery before research 085's milestone (*The + bootstrap delivery, and the one milestone after it*, under *Milestones*; ADR 0644). + +### The revision view + +- §194 **A pair, each half served by its one owner** (ADR 0656). For a trunk revision, or for a candidate's heads before + any merge, the indexer serves the catalog half by the seat `mesh-catalog`'s verb `revision`: the catalog graph, the + heads, the seat definitions, the manifests' verdicts and the fingerprints. The assigner serves the assignment half by + the seat `mesh-wants`'s verb `revision`: an assignment graph revision, named by its own revision and naming its + inputs, the catalog graph revision, the placements and the capability readings. A part's view for the current + revision is the newest assignment half and the catalog half it names. `seat-definitions` stays the seat list's verb + (ADR 0636 §4), and the catalog half carries the same definitions at its revision. +- §195 **Every part takes a first view, then follows the events.** The checker, the builder, the grants part and the + indexer's own later passes take a first view from the two verbs and then follow the two seats' events, never a list + compiled into their build nor another module's bus state (ADR 0656, ADR 0596). The node-engine reads only the catalog + half, for the seat definitions it composes with, and its want; it never reads the assignment half (*Each part acts on + its own entries*, above). Of the view the assigner reads only the catalog half, never its own output back; its other + inputs stay the placements and the capabilities (ADR 0656). A running part acts only on a trunk revision's view; a + candidate's view is read only by the parts that judge a candidate, inside the check its manifest validator and + composer, and the indexer's candidate pass (ADR 0656, which changes ADR 0636 §4 in that the candidate pass joins + them). From G's delivery a running part acts on a revision of a watched branch as on a trunk revision, and a + candidate's view is read only by a run on request's steps and the indexer's candidate pass (ADR 0666 §6, §18). +- §196 **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*, + 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). 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 has no view + today; milestone G adds there a view follower with its read and ask-again tools, beside the proved caller, a + verb's `callers` and a seat's publish, 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. Its cost: a release of the TypeScript side rebuilds every TypeScript module, which + the delivery carries like any change of a package its users build in. + - **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 0663, which changes ADR 0621 §1, §2 and + ADR 0615 §3; accepted); 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). +- §197 **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*). +- §198 **The indexer reads every definition before it judges.** At its start the indexer reads the seat definitions of + every contracts module's trunk through the forge (the seat `git`'s verbs) before it judges a manifest or answers + `seat-definitions`, and only then follows the forge bridge's stream; a definition merged while it was down is + therefore never judged against a list that lacks it. The design's choice under ADR 0636 §1 and §4, as the operator + asked it (1921's note of 2026-11-07: "the indexer reads its seat definitions at start"). How it is checked: in the + indexer's whole-module test, started with a definition on a contracts trunk that no event announced, the first + manifest claiming that seat is judged with it. +- §199 **Who holds a seat is applied by one rule, from what each node runs.** The node-engine and the grants part decide a + seat's pass by mesh-sdk's `SeatsToPassOn` (ADR 0660 §3), reading the manifest of the build each assignment runs + (ADR 0660 §2). What each node runs is heard, never asked: each part keeps it from the node-engine seat's events + `running..` (*What each step says*), through a durable consumer of its own declared in its `consumes`, + so a pass is decided with no call to a node that may be away. The seat's stream keeps the last message per subject + with no age limit, so the last `running` of every module on every node stays on it however long ago it was said, + and each part's durable consumer starts from the last message per subject, never from now: a consumer made after a + node's last `running`, and two parts' consumers made at different times, hold the same builds. The event carries the module, the fingerprint and the + digest of the build's manifest, which is the indexer's manifest of that build (ADR 0660 §2's "the indexer's + manifest of it"), read by digest from the catalog half; the builder's done key is not needed, since the event names + the digest it would give. A node that has said no `running` for a module since the part's consumer began counts by + the trunk's manifest, as ADR 0660 §2 has it where the running build cannot be read, and both parts apply that same + fallback in the one function (ADR 0660 §3). Never another node's have key, its want or the assignment half (ADR 0596 + §2; *Each part acts on its own entries*). The design's choice under ADR 0660 §2: the node that runs a build says what + it runs, and an event kept by a durable consumer answers when that node is away. How it is checked: test 56. +- §200 **A part with no view says it waits.** It names the revision it has no view for, acts on nothing new, and keeps + running what it runs, as ADR 0636 §1 has a reader of the seat list do; the checker, the builder and the grants part + wait too, and wait for the view, not only the seat list (ADR 0656, which changes ADR 0636 §1); the node-engine waits + for the catalog half as ADR 0636 §1 already has it wait for the seat list. Each answers in its runtime's first-view + tool (below) the revision it holds, the one it waits for and since when, and after five minutes without a view writes the finding + `view-waits` naming itself and the revision, as a reader of the seat list does, which the condition keeper raises + (ADR 0564 §2); it clears when the view arrives. Five minutes is the bound the seat list's readers already keep, so an + indexer or assigner down for a restart raises nothing. A part restarted while the indexer or the assigner is down + waits and says so. +- §201 **Each process knows its own build from its start.** The node-engine hands every process it starts the + fingerprint of the build it starts it with, and the process takes its own build from that, never learning it later + (ADR 0656, which changes ADR 0639 §7 from the indexer to every process). *The fingerprint handover*, below, says its + form. + +### What ADR 0656 leaves to this design + +§202 ADR 0656 leaves how each part is built to this design, under issue 1921; these four needed a shape of their own. + +- §203 **An acting rule's fields.** The seat's definition is where ADR 0656 has an acting part declare what it acts on. A + seat's definition gains one optional field `acts`, a list of rules, each with three fields of its own: `step`, one of `take-in`, + `validate`, `check`, `build`, `compose`, `grant` and `place`, and from G's delivery `pipeline`, the runner's, in + `check`'s place (ADR 0666 §19); `on`, either `every` (every module) or a `language` + naming the language of the modules it acts on, matched against what the manifest says its language is, as the + indexer's language map matches today; and `bound`, how long an entry waits in that step for the acting part's new + version (*An acting part's new version acts first*, below, gives each step's default). The definition format's own + schema (design 26) gains the field, and the format stays 1. The indexer reads it into the catalog graph, the assigner + into edges, and the checking program from the revision view. **Its cost in mesh-sdk:** the seat-definition reader in + mesh-sdk's runtime refuses a definition with a field it does not know or a format other than 1, so a reader built + before the field refuses every definition that carries `acts`, and a refused definition blocks new claims of that + seat only (ADR 0636 §3), never what runs. The runtime therefore learns `acts`, which rebuilds every module once + (ADR 0656, Consequences); that is the same rebuild the fingerprint handover makes, so the milestone rebuilds every + module once in all, and no other module's code changes. A definition carrying `acts` reaches only readers built with + that runtime: the seat definitions that gain it are members of milestone G's group, and every reader is rebuilt in + the same delivery; the format's reader itself is delivered before them, as a release of mesh-sdk's seat-definition + reader merged ahead of G's check (the operator's choice of a reader first, 2026-11-09; ADR 0667 §4). The bootstrapped + definitions every reader carries from its start (ADR 0636 §2), of which `node-backup` carries no `acts`, are made + again from the new definitions in the same change, and mesh-sdk's existing test that each bootstrapped copy equals its definition holds + them to it. +- §204 **One wiring of the seat list's reader.** The reader of the seat list and its two tools of ADR 0636 §7 are wired once, + by mesh-sdk's runtime (`go/seatlist`), for every part that reads the list; no part wires its own. Four delivery + parts (the assigner, the checker, the indexer and the grants part) each carry a hand-wired copy today, each with its + own pair of tools, and each copy would have to learn `acts` apart. The design's choice under ADR 0636 §7, which puts + the tools in the runtime bundle: one wiring learns `acts` once, and every part's two tools are the same, named by + their module, with the same callers (any person and the healers to read, the operator and the healers to read + again), the node-engine's among them. How it is checked: mesh-sdk's seat-list test registers both tools for a + fixture part; a search of the delivery parts' code finds no wiring of its own. +- §205 **The fingerprint handover.** The node-engine starts each process, a service or a container alike, with the + environment value `MESH_FINGERPRINT`, the fingerprint of the want it applies; mesh-sdk's runtime reads it once at the + process's start and holds it as the process's own build (ADR 0656). A process started with no value says `own build + unknown` in its runtime tool and its journal, and the indexer acts on it as ADR 0639 §8 has it. **Its cost:** one + change of mesh-sdk's runtime, which rebuilds every module once (ADR 0656, Consequences), and one of the + node-engine; the order between them is the composing step's wait of the node-engine (*Order without a person*, + below), so a module whose runtime reads the value is composed only by a node-engine that hands it. +- §206 **The build seat's ask that runs a program in the sandbox.** The checker asks the seat `node-build-go` for an ask + `run` on the build seats' `check` stream: one whole job on one node (ADR 0525 §3), which fetches the named heads, + builds the named program at them, runs it in the run step (ADR 0448 §2) with the input the ask names by its sha256 in + the asker's own state (the checker's `inputs`, a day), asked of the seat `mesh-delivery-checker`'s verb `input` and + checked against that digest, laid as a file, and writes the program's exit and its answer, carried whole, 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 from G's delivery the runner's and the indexer's (its validation before take-in), each under its own name. + Every step key of a `run` ask starts with the asker's name (`checker.`), 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. From milestone G the seat definition of `node-build-go` in `mesh-delivery-contracts` names it; B0 + brings the ask with its types in mesh-sdk's `go/delivery` and the grants check asks have today (*The bootstrap + delivery, and the one milestone after 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. 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. +- §207 **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 + for a build node` when no build node of the language took the ask within the bound, and `did not build` when one + took it and the build failed, so the operator knows whether to free a build node or fix the change. + +### Choices this design makes under ADR 0656 + +§208 ADR 0656 decides the direction and leaves every choice made in working it out to this design, each with its reason +(ADR 0656, Consequences; the operator, 2026-11-06 20:3x). The choices, and why: + +| Choice | Why | +|---|---| +| §209 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 | +| §210 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 | +| §211 No old form written beside the new; a revert restores the parts' kept data on the bus from a restore point taken before they moved, then they catch up forward | only the new form is looked after (the operator, 2026-11-06 21:3x), and a revert still works, as the delivery system's capability for every change of a kept data's form | +| §212 A module's kept data on the bus backed up before a `form` change and restored beside the live data at a revert, a missing restore point holding the revert | ADR 0404 covers only the items on a module's node, so the bus's kept data had no restore point; a restore never goes over live data, and a revert with nothing to restore must not start on the newer form unasked | +| §213 Only kept data whose `form` changes restored, the wants never; at a revert the node-engine last on its node, and the reverted assigner and gate only after the reverted indexer took in the trunk's head | restoring anything else would set back what was decided since; restored wants, or an assigner reading an indexer not yet caught up, would send nodes to older fingerprints; the node-engine of before may not know how to restore | +| §214 A restore point holding the consumers' positions and the catch-up tool the newer manifest declares, called after a restore | the reverted build then hears everything since as after any restart; the stream `forge` keeps 7 days, so what fell out of it is taken in from the trunk by the part's own tool | +| §215 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 | +| §216 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 | +| §217 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 | +| §218 `record-open` only when no other condition covers the record | the cause is already told once; a second condition for it is noise | +| §219 `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 | +| §220 The switch into records keyed by milestone and heads, safe to run twice | delivering milestone G again after a revert makes no second record and asks no group again | +| §221 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 | +| §222 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 | +| §223 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 | +| §224 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 | +| §225 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 | +| §226 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) | +| §227 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) | +| §228 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 | +| §229 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 | +| §230 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 | +| §231 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) | +| §232 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 | +| §233 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 | +| §234 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 | +| §235 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 | +| §236 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 | +| §237 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 | +| §238 The acting rule's field `acts` with `step`, `on` and `bound`, the definition format staying 1 | the steps and the language match are what the indexer's map and the checker's cases held in code (research 085, Findings 2); the reader refuses an unknown field, so the runtime learns it in the one rebuild the milestone makes | +| §239 The fingerprint handed as `MESH_FINGERPRINT` at a process's start | one value every runtime reads before anything else, a service and a container alike | +| §240 The delivery record's consumer, held and failed outcomes, end and `record-ended`, the finding `record-open`, node away and wake, switch done once, and tools | a restart must lose and double nothing; a reader sees why an entry stands still; a node asleep must not hold a delivery open; a switch run twice must ask no approved group again | +| §241 A small fix's own record | a small fix is a milestone of one issue (ADR 0493 §3) | +| §242 An Approve given held while its heads stand (issue 1972) | the operator approved those heads; a check of them asked again changes nothing they approved (ADR 0441 §1, ADR 0548 §8) | +| §243 The milestone events on a subject of their own | a waiter on one member's pull request otherwise wakes on every member's | +| §244 The issue tracker's link kinds to a decision and a research effort, and the knowledge base's link from a decision to its milestone | the trace from research to the running build is then links all the way, with no text match (research 085, Findings 7; ADR 0493 §4) | +| §245 The view's state read and asked again by mesh-sdk's `go/views` tools `_first_views` and `_first_view_again`, which gain the revision a part holds and what it waits for; the typed first view and the following of each half in the seats' client packages `meshcatalogclient` and `meshwantsclient`, built on `go/views`; the finding `view-waits` after five minutes; and `node_process_builds` | every part's view and every process's own build is read and steered by a tool; `go/views` already follows an owner's events durably and serves those two tools, so a second follower or a second pair of tools would be two forms of one thing; a view tool typed by the two seats would make mesh-sdk import contracts (ADR 0635 §4), so the typed half sits in the seats' client packages (ADR 0625 §4); a part waiting for the view is raised on the bound the seat list's readers keep | +| §246 The seat list's reader and its two tools wired once by mesh-sdk's runtime | ADR 0636 §7 puts them in the runtime bundle; four hand-wired copies would each learn `acts` apart | +| §247 The trunk's heads from the catalog half, and nothing read of the gate by the checker | ADR 0596 §2; with the hold and turns removed nothing of the gate is left for the checker to read | +| §248 The indexer reads every contracts trunk's definitions at its start, before it judges | a definition merged while it was down is never judged against a list that lacks it (ADR 0636 §1) | +| §249 Another node's running build kept from the node-engine seat's `running` events by a durable consumer, the event carrying the fingerprint and the manifest's digest, the trunk's manifest for a node that said nothing | ADR 0660 §2 reads the manifest of the build each node runs; an event kept by a durable consumer answers while a node is away, where a call would wait on it; one fallback in the one function keeps the two parts agreed (ADR 0660 §3) | +| §250 What the gate and the checker owe the forge said as the event `owed` per repository and pull request, each act safe to repeat at the forge, each outcome said as `owed.ended` and recorded, an unanswered act said again at the gate's start and stalled after 15 minutes | ADR 0596 §1, §2 end the watches of the gate's and the checker's keys; an act safe to repeat cannot be lost between the act and its record, where an id recorded first could; an outcome said and recorded leaves no act unseen | +| §251 A merge made at the forge opens a `forge-made` delivery record | its trace and outcomes are kept like every delivery's, and no later merge waits behind a merge the gate never heard | +| §252 Every delivery part and the gate reach the forge only through the seat `git`'s verbs and events, the act verbs granted by the holder's trusted `callers` to the forge bridge alone, a failed read answered as an error | the delivery parts name no other module (ADR 0656 §1, acceptance test 27); the forge's answers are typed in the seat's contract instead of guessed in each caller; the bridge stays the one part that acts on the forge; an error read as an empty answer would hide a forge down | +| §253 A check that failed because a part of the mesh was not heard says `could not run`, naming the part, releases its claim, and is asked again once per heads and part on that part's `running` event, raising `check-could-not-run` after 60 minutes | the operator sees a mesh fault apart from a failing change, and the check is asked again on the one event that can change its outcome, never on a timer (issue 1373) | +| §254 A module held for its claim raises `module-held` until it is composed | a module left out in silence would look delivered (the mesh's rule that failure is loud) | +| §255 The indexer waits for the seat `git` at most 2 minutes at its start, then judges from its kept definitions and raises `view-waits` after five | longer than a forge's restart, and no start held behind a forge down for good | +| §256 The forge before the forge bridge before the gate, reverted the other way | the verbs are served before they are called, and `owed` is said only to a bridge that hears it | +| §257 The store seat's holder's own new build kept on its build node and fetched by `kept-artifact` when its seat is broken | its own build needs the store it replaces; a step of the delivery replaces the hand bootstrap | +| §258 Every drop and release tool writes the hand-act log | a hand act that changes what the mesh runs or keeps is recorded with who did it and why, as every hand act is (seat `mesh-hand-acts`) | + +§259 Each choice is checked by the tests its section names; the review by architectural domain judges them. + +### What each step says + +§260 Each step says what it did as its own event, the event of the part that did it. A catalog graph revision kept +carries the source revision; an assignment graph revision kept and a difference worked out carry the source revision +and the assignment graph revisions; an entry taken in, built, checked, composed, wanted and running also carries the +entry's fingerprint and its node. The gate's delivery record is built from them. The seat's event comes first where +the parts follow it through a seat (ADR 0656); a holder's own event stays under its own subject, declared in its own +manifest's `streams` and published on `mesh.stream..event.` (ADR 0363 §1, §2). A seat's event is +declared in its definition and its holder's manifest claims the seat; a seat event's name has no dot, since it is read +as a subject's token, and it is kept on the stream `SEAT__`, which the grants part makes with its durable +consumers, keeping it by the age and cap the definition's `keep` names, 90 days unless it names less. **An event a node publishes carries the node in +its subject**, and the grants part grants each node-engine's account only its own node's subjects, so one node cannot +say another runs a build (ADR 0363 §2); the gate's events are granted to the gate's account alone. + +| Step | Event | Owner: the seat, then the module | Subject | Declared in | +|---|---|---|---|---| +| a catalog graph revision kept, saying whether it is a trunk revision or a candidate's and its watched branch | `catalog-revision` | `mesh-catalog`, the indexer | `mesh.seat.mesh-catalog.event.catalog-revision`, the watched branch in the subject from G's delivery | the seat's definition in `mesh-delivery-contracts` | +| an assignment graph revision kept | `assignment-revision` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.assignment-revision` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | +| a difference worked out | `difference` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.difference` | the same | +| an entry taken in | `take-in.taken`, which gains the entries | the indexer's own | `mesh.stream.mesh-delivery-indexer.event.take-in.taken` | the indexer's manifest | +| an entry built | `build.ended` | the builder's own | `mesh.stream.mesh-delivery-builder.event.build.ended` | the builder's manifest | +| an entry checked | `checked` | `mesh-delivery-checker`, the checker | `mesh.seat.mesh-delivery-checker.event.checked` | the seat's definition in `mesh-delivery-contracts` | +| an entry wanted | `wanted` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.wanted` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | +| an entry composed, and running healthy | `composed`, `running` | `node-engine`, the node-engine on each node | `mesh.seat.node-engine.event.composed.`, `.running..`, the latter on a stream kept last per subject with no age limit | 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.` | the same | +| 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..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..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.` | the seat's definition in `mesh-delivery-contracts` | +| a milestone's ask asked, withdrawn and answered, until G's delivery | `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.`, the milestone in the event, on the seat's family stream, apart from each delivery's `transition` | the same | +| a delivery record opened, and a merge named a recovery | `record-opened`, `recovery-merged` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.` | the same | +| a pipeline, its steps, its waits and a run on request, from G's delivery | `pipeline-started`, `pipeline-ended`, `group-ended`, `run-started`, `step-started`, `step-ended`, `wait-held`, `wait-ended` | `mesh-pipeline-runner`, the runner | `mesh.seat.mesh-pipeline-runner.event....` | the seat's definition in `mesh-delivery-contracts` | +| a pipeline step ended, from G's delivery | `step-ended` | each build seat, `mesh-wants` | `mesh.seat..event.step-ended.`, the node that ran it | each seat's definition | +| a registration changed; a package published, from G's delivery | `registration-changed`; `package-published` | `mesh-catalog`, the indexer; `mesh-builds`, the builder | `mesh.seat.mesh-catalog.event.registration-changed`; `mesh.seat.mesh-builds.event.package-published` | the seats' definitions in `mesh-delivery-contracts` | + +§261 The events, their fields, their subjects and which are a seat's are this design's choices: a seat's event where +other parts follow the step through the seat, a holder's own event where only the delivery record hears it, so that +nothing reads another module's bus state (ADR 0596). The builder's waits go on its own stream because the builder +holds no seat of its own; the watchman and the gate follow them there. How it is checked: each seat definition's +conformance fixtures name the subject of every event it gains, and a case of the holder's whole-module test matches an +event it publishes against them, as for every seat (design 26); the gate's whole-module test hears each row through +its durable consumer, with exactly the fields this section gives it; a `running` event published by one node's account +on another node's subject is refused by the bus. + +### Their tools + +| 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), `assigner_puts` (each put with its step, since when, held or not, the `running` that ended it, and for a put without hold whether it is past its 30-minute bound; a put older than the trunk's newest build of its module said so) | `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` | + +§262 Every drop and release tool writes its act, who did it and why, in the hand-act log of the seat `mesh-hand-acts`. +Beside ADR 0636 §7's two tools in mesh-sdk's runtime bundle, which read a reader's seat list and make it read the +list again, the view is read and steered by the two tools mesh-sdk's `go/views` already serves every part that takes a +first view: `_first_views` gains which view revision the part holds, what it waits for and since when, beside +what it heard and how far it is behind, and `_first_view_again`, granted as ADR 0636 §7's second is, makes it +read the view again now. `go/views` stays seat-neutral, naming no seat and no seat's type (ADR 0635 §4); the typed +first view of each half and the following of its events are the seats' client packages, `meshcatalogclient` for +`mesh-catalog` and `meshwantsclient` for `mesh-wants`, each built on `go/views` (ADR 0625 §4), so no part follows the +view by a follower of its own. A tool +of the node-engine, `node_process_builds`, reads the build each process was started with. The tools answer the four +lines every module's tools answer (design 26), so that every state these parts keep is read and every act they do is +steered by a tool. + +§263 **How it is checked.** Unit (assigner): a revision of the assignment graph computed again from its named inputs +equals the one kept; a fixture change with a new fingerprint and an empty difference is refused, naming its module; +one revision of about 120 modules on four nodes stays under the bus's value limit; of the view the assigner reads only +the catalog half. Unit (indexer): a catalog graph revision holds one entry per module of its source revision. Whole +(indexer): a source revision taken in gives one catalog graph revision kept as objects in an `objects` section in the +old form within the cap its manifest declares; the manifest validator refuses the indexer's or the assigner's manifest +that declares them in the new form. Whole (indexer, assigner): a revision a delivery record pins is not removed; one +two records pin stays until both free it; one neither pinned nor current is removed; a `freed` said while the part +was stopped is applied after its restart; a candidate's graphs are dropped on `heads-replaced`; a store past 80 % says +`filling` and raises `store-filling`, and a full one says `full` and refuses its next revision naming itself; for a +candidate's heads both `revision` verbs answer, the assigner's half named by its own revision and naming its inputs; a +placement change with no merge, and a `capabilities-changed`, each give a new assignment half on the same catalog half, +whose difference moves only the module concerned, and a part takes that pair as its view; after its first view a part +acts on a seat event without asking a verb again; a running part does not act on a candidate's view; a pull request +that adds a seat and a module using it is validated by the indexer's candidate pass and checked by the checker with +the same definitions; the tools of the table answer and the drop tools write the hand-act log. Whole (assigner): a +pull request's graph leaves every node's want unchanged; a placement-only revision moves the want of the module it +places; the builder and the grants part read their entries only through the seat `mesh-wants`'s verbs and events, and +the node-engine reads its want and the catalog half and never the assignment half. Whole (node-engine): a part started +while its node's want differs from what runs reports the build it was started with. Live: a search of the readers' +code finds none of the reads the revision view replaces, each by name: the indexer's read of the checker's previews, +the checker's watch of the indexer's `candidates.`, the forge bridge's read of the gate's `merges` and `owed`, +the checker's read of the gate's `source` and `turns`, the forge module's watch of the checker's `owed` (its `mesh/check` +status, now the checker's `owed` event performed by the forge bridge) and its read of the gate's `source`, the grants +part's read of the assigner's state `wants`, any module's read of the indexer's `modules` (its tool +`indexer_read_modules` in an `invokes`, its view in a `consumes`), the +checker's validator request with no seat definitions, a seat list compiled into the node-engine, the checker, the +builder or the grants part, a seat list's reader wired by a part itself, and a call of the forge module's own tools by +a delivery part or the gate; an old node-engine that wakes takes its new build in a form it reads. + +## Order without a person: the acting parts + +### An acting part's new version acts first, a wait with a bound + +*ADR 0656, which supersedes ADR 0646; ADR 0603 §1 to §4, §8, §11, ADR 0614 §1, §3, §6, ADR 0633 +§1, ADR 0539 §7, ADR 0584 §1, §2, ADR 0564 §2.* + +§264 From research 085's milestone. A seat's definition may carry a rule, the step its holder acts in and the modules it +acts on, and a holder of such a seat is an **acting part** (ADR 0656): the indexer taking in and validating, the +checker checking, the build modules building, the node-engine composing, the grants part granting, the assigner +placing. *What ADR 0656 leaves to this design*, above, gives the rule's fields. Adding one needs its manifest and +its seat's rule, and no code elsewhere: the indexer's map from language to build module and the checker's own module +names go, since every new acting part needed code changes elsewhere (research 085, Findings 2). + +- §265 **The rule becomes edges between versions.** In the assignment graph the assigner gives an edge from the acting + part's version to each version its rule names, and no part names another module in its code to order by (ADR 0656). +- §266 **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. 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; from G's delivery 30 for the runner's `pipeline`, whose wait ends when the new runner runs healthy, one module's apply on one node as composing's is (the design's choice under ADR 0666 §19, 2026-11-10). 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). +- §267 **A build module's wait ends on the agreed toolchain.** For building, an entry waits until the agreed toolchain of + its language (ADR 0584 §1) names the new build module, never merely until the new build module runs on one node: a + fingerprint names the agreed toolchain (ADR 0603 §3), so only then is the module built once, by the new one. A build + 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. +- §268 **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..` 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. +- §269 **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 + 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 + 4; stopping the whole delivery would hold modules the failed part does not act on (the operator, 2026-11-06 17:0x). +- §270 **A wait holds only within its step, and skips the holder's own inputs** (ADR 0656). An entry waits only for its + own step's holders, so two acting parts that act on each other in different steps (the indexer taking in the + checker, the checker checking the indexer) form no cycle. Within a step a holder's own change is its first act, and + what its new version is built from or needs (a base, a package, a provider) is acted on by its running version, + never waited for: the running build module builds the shared contracts and the registry holder, then the new build + module builds the rest, so ADR 0614 §6's cycle through them stays allowed, now for that reason. +- §271 **The artifact store's holder takes its own new build even when its seat is the one broken.** A build module + stores the artifacts it builds through the seat `oci-artifact-registry`, so the + holder of that seat is in its own build's path: its new build needs the store it replaces. As every holder's own + inputs are (above), that store is acted on by the running holder: the running holder stores its new build, and only + then is the new one wanted. When the running holder is what is broken (the seat refuses or does not answer a store, + as in the hand bootstrap of 2026-11-07 to 11-09), the build module keeps that one build on its own node among its + kept builds (*The bootstrap delivery*: `build_go_kept`), says in its step key that it is kept on that node, and the + builder's done key names it so with its digest; the node-engine composing the holder fetches it through the build + seat's verb `kept-artifact` of that node, and the new holder, once it runs, takes the store of every build after it. + - The verb sits in the build seats' definitions (`node-build-go`, `node-build-node`, `node-build-image`, in + `mesh-delivery-contracts`), and its callers are the node-engines alone: the definition names them, and the grants + part grants it to no other account. + - It answers only for a module that claims the seat `oci-artifact-registry`, and refuses every other, naming the + module, so a store outage never turns into delivering from build nodes. + - The bytes go as the reply's object in the build module's sent store `_sent-objects` (*Any size*), never in + a value, since an artifact is over the bus's value limit; the build module is no foundation part, so it keeps no + old `objects` section, its kept data being in ADR 0595's templates (ADR 0666 §4), and the build itself stays among + its kept builds on its node (the design's choice, 2026-11-10, correcting the old form this line named); the node-engine checks the bytes it fetched against the digest in the + builder's done key and applies nothing that differs. + - While the keeping node is away, or its sweep removed the build, the want waits, and the node-engine says in its + have that it waits for that node or for a build no longer kept. + + This is the one artifact the mesh delivers from a build node instead of the store, and only for the holder of + `oci-artifact-registry`; the design's choice under ADR 0656 §3 (a wait skips what the holder's own new version needs) and ADR + 0526 §2, whose build by a session's call it makes a step of the delivery instead of a hand act. How it is checked: + test 60. +- §272 **The check refuses a cycle among the waits within one step** (ADR 0656, which changes ADR 0603 §2's refusal of a + cycle), counted beside the edges ADR 0614 §3 and §6 list; a cycle through an acting part's edges across two steps, or + through a holder's own inputs, is none. +- §273 **The indexer's own steps have no wait, and a newer indexer is reached.** Taking in and validating stay as *Taken in + 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 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. +- §274 **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. +- §275 **An acting rule grants nothing.** A holder's bus grants come from the grants part as today (ADR 0539 §7). An + acting rule changes what a part acts on across every module it matches, so the operator's Approve ask of a change + names any acting rule it adds, and any it widens: a `step`, an `on` or a longer `bound` that now matches more + modules or holds them longer is named like an added rule. From G's delivery there is no Approve ask per merge, and + the pipeline's end names it in `widens` instead, refusing nothing (ADR 0666 §23). +- §276 **Each step's wait is its own part's, read and released by its own tools.** The part that does a step holds its + wait, so each wait has one owner that already holds that step's state, and says `wait-held` and `wait-ended` (*What + each step says*) with the step, the acting part, its version, the entry and since when: + + | Step | Holder | Reads its waits | Releases one | + |---|---|---|---| + | 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 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. +- §277 **How it is checked.** Whole (assigner): a fixture seat declaring a rule, with a fixture holder, gets its edges with + no code change elsewhere. Whole (assigner, builder, three build nodes): a group changing the shared contracts and the + build module that builds them, the new build module updated on the first build node first, builds no module with the + 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, + 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 (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 + through a build module's edge each pass the cycle check, each new version first in its own step; a cycle among + waits within one step is refused. Unit (assigner): the Approve ask of a change that adds an acting rule, and of one + that widens one, names the rule. Live: a search of the delivery parts' code finds no other module's name. + +## Nothing merges into a repository under check: removed + +§278 **Removed at research 085's milestone.** The hold, the merge turns, their stalled line, the gate's tools for them and +the setting `check-gates-merge` go in milestone G, the gate's and the forge module's alike (the operator, on Telegram, +2026-11-09 01:11 UTC, choosing "Remove them in G"; the forge's merge then judges no check, *The seat `git`*). The setting has been false since 2026-10-25, so the gate wrote no turn and held no merge in that time, and the +operator's word for G is that no check gates delivery (1921's note of 2026-11-07). From G's delivery no merge waits on a check, and none on an Approve either (ADR 0666 §10): the checker is gone, and the +pipeline runs on the landed commit (*Pipelines per module*). **Its record is accepted:** ADR 0661 ends ADR 0548 §2 +to §4, §9 and §10 and ADR 0551 and changes ADR 0534 §4's judgement of `mesh/check` by the merge; until G's delivery the +rule below holds as those records have it. ADR 0662 is withdrawn; G builds ADR 0661 §1, §2 and §6, and ADR 0666 §10 +changes its §3 to §5 (the merge on the Approve alone), which G therefore builds for no merge. Beside it, ADR 0663 (accepted: the indexer answers `revision` in its own runtime to +exactly the parts that invoke it, the tool runner among them) holds the tool +runner's first view of the catalog half (*Every other reader of which modules exist*). How it is checked: in the gate's whole-module test an approved merge into a +repository under a running check is handed at once, and the gate's manifest declares no `turns` and no setting +`check-gates-merge`; in the checker's, a check claims at once with no gate state granted to read. + +## Checks: the check on the change's own acting parts + +### The check on the change's own acting parts + +*ADR 0656, which supersedes ADR 0648, ADR 0644; ADR 0448 §2, §4, ADR 0391 §1, ADR 0603 §10, ADR 0614 §7, ADR 0633 +§6, ADR 0636 §4, ADR 0639 §5, ADR 0498 §1.* + +§279 From research 085's milestone. ADR 0603 §10 and ADR 0614 §7 let a check pass while the part that would build or +check the change never ran: 7 of 24 failures since 2026-11-01 (research 085, Findings 4). The acting parts a change +alters, and the checking program, the rules every check applies, are built from the change's heads and check it; the +running versions never check in their place, and a check fails, naming the part, when one cannot be built or run +(ADR 0656). + +- §280 **Two tiers on the change's assignment graph.** The first tier builds, at the change's heads, only the acting parts + whose fingerprint in the change's assignment graph differs from the running one and which are to act on a module + the check acts on; a change to an ordinary module asks no first-tier build, since building every acting part for + every check would make an ordinary change pay for builds that change nothing. The second tier runs the check's + levels with those programs and with the revision view of the change's heads (ADR 0656). This keeps ADR 0603 §10's + building of changed build modules first and widens it to every acting part. +- §281 **The judging rules are a checking program built from the change.** Every check builds its checking program from + the change's heads and runs it with the revision view as its input; when it cannot be built or run, the check + fails naming it, and the running checker's own rules never judge in its place (ADR 0656). It is built once per + fingerprint and reused (ADR 0498 §1): checks whose heads leave the checking program unchanged ask one build between + them. +- §282 **Every program built from a change runs only in the build seat's sandbox.** The acting parts the first tier builds + and the checking program run in the run step, with no network (ADR 0448 §2) and no bus account, the logins staying + outside (ADR 0448 §4), through the seat `node-build-go`'s ask `run` (*What ADR 0656 leaves to this design*), as + the validator, the composer and the Go build module already run; a change not yet merged is not trusted. The running + checker alone asks, reads the program's verdict, records it and publishes it. +- §283 **A first tier that cannot build an acting part fails the check, naming it.** The verdict never passes on the + running version's word: a module whose changed build module the check cannot run is no longer composed from its + manifest (ADR 0656, which changes ADR 0603 §10 and ADR 0614 §7), and ADR 0614 §7's reason, a changed build module + 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`. +- §284 **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. +- §285 **ADR 0633 §6 stays, beside the change's own validator.** Whether the running parts can take the change in is + still checked with the trunk's manifest validator, built from the trunk (ADR 0633 §6); the change's own validator, + built in the first tier, judges the change's manifests besides (ADR 0656). A change fails if either refuses it. +- §286 **The asking and recording are covered by the checker's whole-module test**, which starts the changed checker on a + real bus and asks one check of a fixture group end to end (ADR 0391 §1). +- §287 **One seat list for both readers of a change's heads.** Inside every check, a lone pull request's as a group of one, + the checking program built from the heads makes one seat list from them, and the checker hands it to the manifest + validator and the composer (ADR 0644, ADR 0636 §4 with its insight of 2026-11-06); the running checker reads no + definition of the heads itself (*The bootstrap delivery, and the one milestone after it*). +- §288 **Its tools.** The checker's read of a check names the checking program's build that judged it, its fingerprint and + the heads it was built from, and the rules named changed; `checker_first_tier` lists a check's first-tier builds, + each with its acting part, its fingerprint and its outcome (`built`, `waiting for a build node`, `did not build`), + and asks one of them again. Asking a whole check again stays the checker's existing ask-again. +- §289 **What it costs.** A check of a change to an acting part costs one build per changed acting part, within the + checker's step bound (ADR 0639 §5); an ordinary change costs nothing more; a checking program costs one build per + fingerprint, shared by every check that uses it. +- §290 **How it is checked.** In the checker's whole-module integration test: a fixture change whose acting part does not + build, and one whose checking program does not run, each fail their check, naming the part; a checking program that + 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 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 + same checking program ask one build of it; a group that changes the assigner's placing is checked with its own + assigner; a fixture indexer change whose manifest the trunk's validator refuses fails its check, even when its own + validator passes it; a fixture group whose heads add a seat has that seat in the list both the validator and the + composer receive; the check's read names the checking program's build, heads and changed rules; `checker_first_tier` + lists a check's first-tier builds and asks one again; the changed checker asks one check of a fixture group end to + end. Live: the registry group passes its one check with its own checker, validator and composer. + +## After the merge, from milestone G + +- §291 **From milestone G's delivery, per watched branch.** The gate writes one source revision per watched branch, + `source.`, as a commit or a group lands there; the pipeline runs on each revision the indexer takes in, and + the wants follow a revision whose `required` steps passed, on the branch each placement chose (ADR 0666 §6, §9). +- §292 **From research 085's milestone, the wants follow the trunk's assignment graph.** A merge taken in makes a + catalog graph revision and then an assignment graph revision; the assigner writes each want from that revision, + its rules unchanged (ADR 0656), and each acting part's new version acts first in its own step (ADR 0656; *Two + graphs, one source of truth*, above). + +## Taken in by the indexer it names, from milestone G's delivery + +- §293 **From milestone G's delivery** (ADR 0666 §6, §13, which change ADR 0633 §1 and §3 to §6): before any take-in, on every + watched branch, the running indexer validates the revision with the manifest validator built from its heads, in the + sandbox, and a revision it refuses stays pending, as the bullets above have it, with the way back a revert; a pending + revision holds only its own watched branch; a group's member reaches it by landing, and the group ends when the gate's + group verb says every member has; `bridge_group_end_again` reads that verb; and the check's refusal below goes with the + check (*Watched branches and the catalog graph per branch*, *Groups*). + +## A backup before the apply: kept data on the bus + +### A module's kept data on the bus, backed up and restored with its delivery + +*ADR 0404, ADR 0235, ADR 0233, ADR 0539 §16, ADR 0595, ADR 0619 §2, ADR 0633 §1, §5, ADR 0363 §2; from research +085's milestone, this design's choice under ADR 0656.* + +§294 **ADR 0404's backup does not cover a module's kept data on the bus.** It backs up the items a moved module keeps on +its own node (ADR 0404 §2, ADR 0233), and the bus's streams are dumped whole by the bus module on its own rhythm +(ADR 0235), not per module and not before a delivery. A key-value state, a stream or an object store a module keeps on +the bus (ADR 0595, ADR 0619 §2), and its durable consumers' positions on the streams it reads, therefore have no +restore point taken before its module moves. That is a gap of the delivery system, and milestone G builds it, because +a revert of milestone G is the first change that needs it. It is built as the delivery system's capability for every +later change of a kept data's form, not as a case of this milestone. + +- §295 **Its form is declared.** Each kept data a manifest declares names a `form`, a number its holder raises whenever it + changes how it writes that data, or stops declaring it; a declaration naming none reads as form 0. The declaration + alone cannot tell a changed layout of the same bucket, so the holder says it. Only kept data whose `form` changes is + backed up and restored, so a part's other kept data, and every decision it holds, is never set back. +- §296 **It is backed up before the module moves.** When a want moves a module whose new manifest declares a kept data at + another `form` than the running one, or no longer declares it, the node agent applying it first asks the seat + `node-backup` of the node holding `mesh-broker` for a restore point of that kept data, and waits for its event + `ended`, as for any backup before the apply. The bus module's account takes it, as it takes the bus's snapshots + (ADR 0235 §5). A restore point is keyed by the module, the kept data, its form and the fingerprint it was taken + before, and it holds the kept data whole, the position of each durable consumer the module reads with, and the + catch-up tool the newer manifest declares beside that kept data. A failed + backup holds that module as any failed backup does. +- §297 **A revert restores it before the reverted build starts.** When a want takes a module back across a change of a + kept data's `form`, the node agent applying it asks that seat's verb `restore` for the restore point of that module, + kept data and form, waits for the seat's event `restored`, and only then starts the reverted build. The node agent + that restores is the one running when the revert starts. As every restore is, it is brought back beside the live data + and never over it: the live kept data of the newer form is set aside under a name of its own, and only the operator + removes it, so nothing written since is destroyed. The consumer's position is set back with it, so the reverted + build hears everything since as after any restart. +- §298 **The order of a delivery that crosses a form change** is the module graph's (*Order without a person*), with three + edges the assigner, which writes every want, keeps: + - the new versions of the `node-backup` holder and of the bus module run before any module whose kept data's form + changes is moved, since the restore point needs both; + - at a revert, a node's node-engine goes back last on that node, after every module it restores there has said + `restored` and runs, since the node-engine of before may not know how to restore; + - the reverted assigner and gate are wanted only once the reverted indexer has taken in the trunk's head, as its + `take-in.taken` naming the current source revision says (ADR 0633 §1), since the assigner of before computes the + wants from the indexer's `modules` and would otherwise write the fingerprints of the restore point. Until then the + running assigner writes no want, and every module keeps the fingerprint it runs. +- §299 **The restored part catches up forward, with the code it already has.** Read on main, mesh-delivery at 2d201f78 + and mesh-catalog at 1d07778c: + - **The indexer** (mesh-delivery, `modules/mesh-delivery-indexer`): its durable consumer `_` on the stream + `forge` redelivers every message since the restored position, and `handle` takes each + merge through `Indexer.Arrive` into `pending` and then into `modules` (`applyMerged`); `applySource` takes the + gate's `source.main` in, and `ApplySource` leaves a revision older than the latest it has seen. **The stream + `forge` keeps 7 days**: the forge bridge's manifest declares no age, and a stream's default is 7 days, at most 30 + (mesh-sdk `go/catalogue` `StreamDefaultDays`, `StreamMostDays`), so it does not cover the records' 90 days. A + merge older than the stream's first message is never redelivered, and the code then leaves that repository's + modules at the restore point's fingerprints. So the restore runs one forward step: once the reverted indexer + runs, the node agent that restored it calls its tool `indexer_take_in` with each repository the gate's source names + (`NewTakeInRepository`, which takes in the head the source names for a repository). The catch-up tool is declared + by the newer build's manifest, beside the kept data whose form it changes, and recorded in the restore point when + the backup is taken; the node agent takes it from the restore point, never from the manifest being applied, since + at a revert that is the manifest from before the change, which names none. So the delivery system names no + module's tool. + - **The assigner** (mesh-delivery, `modules/mesh-delivery-assigner`): it keeps nothing restored. Its pass + (`passer.wants`) writes, from what the world holds now, each node's want by the six rules from the indexer's + newest fingerprint at the trunk, and only where the value differs; so once the indexer has caught up it writes + the trunk's wants, and nothing older. + - **The gate** (mesh-catalog, `modules/mesh-delivery-gate`): `Holder.Load` reads the restored deliveries and groups + back; `readOnStart` then reads what happened while it was away: `wantForge` reads the forge's open pull requests + at the next look, `hearAsks` reads the teller's answer to every ask it still holds as pending, and + `SourceCatchUp` brings the newest merge of each repository into `source.main`, never moving a repository back. An + ask the gate of milestone G raised after the restore point is not among the pending ones, so its answer is not read + and the operator is asked again. Whether the first look clears a merge kept in the restored `merges` that the forge + merged since was not read in the code; the catch-up test below proves it with the real builds. +- §300 **A missing restore point holds the revert, naming it.** When the restore point a revert needs does not exist, or + `restored` does not come, the node agent applies nothing of that module, keeps what runs, and writes the have phase + `held` with the reason `restore point missing` and the module, kept data and form it names; the assigner asks the + operator (hold, or start the reverted build on the live data as it is), raises the condition naming them, and on + the answer writes the node's want again naming it (ADR 0539 §16). +- §301 **What the operator is told.** A revert across a change of a kept data's form puts that kept data back to the + moment before the change moved it: the deliveries made since stay delivered, but what the part wrote there since + (for milestone G, its delivery records and its graph revisions) is set aside and not read again; an Approve asked + and answered since is asked again, and one the gate asked before still reads its answer. The revert's Approve ask + says this in those words, naming each kept data restored. +- §302 **Its tools.** The seat `node-backup`'s `backed-up` lists each module's restore points of its bus data, with the + kept data, the form and the fingerprint each was taken before, and the data set aside at each restore, with its + size; `restore` restores one; `discard-aside` removes data set aside by a restore, only on the operator's call, and + writes the act, who and why in the hand-act log of the seat `mesh-hand-acts`. `backed-up` and `restore` exist on + the seat today and gain the module's kept data on the bus as an item; `discard-aside` is new. +- §303 **How it is checked.** Whole (node-engine, backup holder, bus module): a want moving a module across a `form` + change, and one moving a module whose kept data declares no `form` to one that does, each take a restore point + first, keyed by module, kept data, form and fingerprint; a want taking it back restores it beside the live data and + the consumer's position before the reverted build starts, and the data set aside is listed with its size and + removed only by `discard-aside`, which writes the hand-act log; a revert of a group holding the node-engine and a + module whose form changes restores the module before the node-engine goes back; a revert whose restore point is + missing is held, naming it, and moves on only on the operator's answer. The catch-up is proved with the real builds + of the indexer, the assigner and the gate from before milestone G, never fixtures (acceptance test 36). + +## The verbs milestone G adds + +§304 From research 085's milestone the seat gains: + +| Verb or event | Answers or does | +|---|---| +| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656), and the acts the gate said it owes the forge for it, each with its id and its recorded outcome (*What the gate owes the forge*) | +| `owed` | an act the gate owes the forge (a merge, a status, the delivery's view, a comment, a line on the commit's note), with its pull request, head and id, on `mesh.seat.mesh-delivery.event.owed..`, said only from the gate's account and heard by the forge bridge through a durable consumer (*What the gate owes the forge*) | +| `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | until G's delivery: the one Approve ask per milestone (ADR 0493 §9 as ADR 0659 §1 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.`, the milestone in the 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`. G's delivery ends the ask and its three events with the Approve per merge (ADR 0666 §10) | +| `record-opened` | a delivery record opened, which the watchman times for `record-open` | +| `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`, `room` | a delivery record ended; the ledger past 80 % of its cap; the ledger full; the ledger back below its cap (*The delivery record*) | + +§305 From milestone G's delivery the seat's `table`, `recheck` and `close` go with the merge states (*The state table*), +the milestone ask and its three events with the Approve per merge, and it gains (ADR 0666 §6, §26): + +| Verb or event | Answers or does | +|---|---| +| `source` with a watched branch | that branch's source revision; the indexer and the node-engine's catch-up read it, never the gate's state | +| `source-moved` | a new source revision of a watched branch | +| `recovery` | marks a merge its author names a recovery (the commit and why), its record marked `recovery` with the caller the runtime proves | +| `record-opened`, `recovery-merged` | a delivery record opened; a merge named a recovery, which the teller passes on to every channel | + +§306 **The seat `mesh-pipeline-runner`**, from milestone G's delivery, defined in `mesh-delivery-contracts` with the callers +of each verb (ADR 0666 §7, §20, §25): + +| Verb | Answers or does | Callers | +|---|---|---| +| `run` | the run on request of one or more members, each a module at a named commit; delivers nothing | the sessions (the agents' bus identities the tool runner proves) and the operator, nothing else | +| `start` | a delivering pipeline for a watched branch's head, by hand | the operator, the healers | +| `show`, `list`, `stands`, `why-carried`, `kept` | one pipeline whole with its step log; the pipelines and runs of a module, a branch or a commit; what stands still; where a result was carried from; the kept results | any person, an agent session, the healers | +| `review` | a code review's result for a commit and fingerprint | the reviewers the definition names; refused from the commit's author | +| `rerun`, `accept`, `cancel`, `ask-again` | one step again; a failed `optional` step accepted with why; a pipeline ended, its asks dropped; a declined `approval` asked again | the operator, the healers (`accept` and `ask-again` the operator) | + +§307 **The seat `mesh-catalog`** gains, from milestone G's delivery, `register`, `policy`, `watch`, `unwatch` and +`registrations`, and the event `registration-changed` (*A module's registration*), and `catalog-revision` names its +watched branch in its subject. **The build seats** gain the verb +`steps` and the accepted kind `step`, and **the seat `mesh-wants`** the kind `step`, each saying `step-ended`. **The +seat `mesh-builds`**, new and held once for the mesh, says `package-published` (*A publish rebuilds*). + +§308 **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 +each step says*. + +§309 **The seat `node-engine`**'s event `running..` carries, from research 085's milestone, the module, its +fingerprint and the digest of its build's manifest, so the node-engine of another node and the grants part decide a +seat's pass from it (ADR 0660 §2; *Who holds a seat is applied by one rule*). No verb is added. + +§310 **The seat `git`.** The seat `git` (design 26, *The git seat*), of the Delivery domain, defined in +`mesh-delivery-contracts`, holds no verb today, and the delivery parts and the gate reach the forge by naming the forge +module's own tools and events and guessing the shape of their answers (on 2026-11-09 one guess, an answer wrapped in a +`result` field, made 316 conditions stand, issue 1966). From research 085's milestone the seat's definition carries the +verbs and events below, their inputs and answers typed in its contract, served and said by the module that claims the +seat; every delivery part and the gate call and hear the seat, never the forge module by name (acceptance test 27). +They are this design's choice under ADR 0596 §1, §2 (a module's events are subscribed to and its data asked through +its seat verbs) and ADR 0625 §1 (a seat's contract is generated from its definition). The table is built from what the +parts' manifests declare in `invokes` and `consumes` and what their code calls, read on main on 2026-11-09 (mesh-delivery +042b8d1f, mesh-catalog's gate); a declaration nothing calls (the checker's `gitea_commits`, `gitea_compare`, +`gitea_list_branches`) gets no verb and is dropped from its manifest: + +| Verb | Answers or does | Called by (today's call) | +|---|---|---| +| `pull` | one pull request, by its repository and number or by its merge commit: number, base, head branch, head, merge commit, author, state, and `merging` while the forge merges it; an unknown one answers the error kind `not-found` | the forge bridge (a merge the gate did not make; `gitea_api`), the gate (a stalled line's author) | +| `branch` | from milestone G's delivery: a branch's head, and whether a protection rule refuses a direct push and admits only merges | the indexer (`register`, `watch`), the forge bridge (a watched branch's head) | +| `open-pulls` | every open pull request of every repository, its head and base, the statuses its base requires and those its head has | the forge bridge at its start and each reconnect (ADR 0443 §5), the indexer, the gate (`gitea_open_pulls`) | +| `tags` | a repository's tags, paged, the page's bound said when more exist | the forge bridge, for the builder's floor (`gitea_api`) | +| `trees` | the trees of one commit | the forge bridge, the checker, the indexer (`gitea_commit_trees`) | +| `file` | one file at one commit | the forge bridge, the checker, the indexer (`gitea_get_file`) | +| `contains` | whether one commit contains another | the indexer, the gate (`gitea_contains`) | +| `statuses` | the statuses a commit carries | the gate (`gitea_commit_statuses`) | +| `status`, `comment`, `view`, `note` | a commit status, a comment, the delivery's one view comment, a line on the commit's note under `refs/notes/mesh-plan`; a comment and a note line carry the act's id and an id already held is skipped | the forge bridge alone, performing the gate's and the checker's `owed` (`gitea_commit_status`, `gitea_add_comment`, `gitea_delivery_view`, `gitea_note_append`) | +| `merge-pull` | until G's delivery: merges a pull request at the head the operator's Approve names; refuses a head that moved, a branch no rule guards, a head the Approve does not name and a merge git refuses; judges no check (below). From G's delivery: merges with the forge tools' account (ADR 0398 §2) at the head the caller names, judging that it is still the pull request's head and that the branch's protection refuses a direct push, the protection read again on every `branch-moved`, and refusing a head when any run at its build-input fingerprint failed a `required` step, judged from its own state `failed-steps` heard from the runner's `step-ended`, never by a call; a head with no run yet or a passed one merges, it never waits for a run and never asks an Approve (ADR 0666 §11). A pull request already merged at that head is answered performed | until G's delivery the forge bridge alone (`gitea_merge_pull_request`); from it any session the tool runner proves, and the operator | + +| Event of the seat | Says | Heard by (today's event) | +|---|---|---| +| `pull.updated`, `pull.merged`, `pull.closed` | a pull request's new head, its merge with the merge commit, its close | the forge bridge, the gate (`gitea.pull.*`) | +| `forge.pull`, `forge.status`, `forge.delete` | the forge's own webhook of a pull request, of a commit status, of a deleted branch | the gate (`forge.pull`, `forge.status`), the forge bridge (`forge.delete`) (`gitea.forge.*`) | +| `repo.writable` | a repository the forge can write again | the gate (`gitea.repo.writable`) | +| `branch-moved` | from milestone G's delivery: a branch's new head, by a merge or an admitted push, from the forge's push webhook | the forge bridge | + +§311 Each is said on `mesh.seat.git.event.`, declared in the seat's definition, and heard through the hearer's +`consumes` of the seat. + +- §312 **A failed read answers an error, never an empty answer.** When the forge does not answer, refuses or answers what + the contract cannot type, the verb answers an error naming why; an empty list or an absent file is answered only + when the forge said so. A caller never reads an error as "none". How it is checked: the seat's fixtures hold an + error answer for each verb, and each caller's unit table has a row for it (the forge bridge, the checker, the + indexer, the gate). +- §313 **Who may call an act.** The holder's trusted config value `callers`, which today names per verb who may call the + two package seats' verbs (design 51), gains a line per verb of the seat `git`: the act verbs name the forge bridge + alone, the reads name each part that declares them in `invokes`; the grants part grants by it, and the holder + refuses a caller it does not name. From G's delivery the callers per verb are the seat definition's field `callers`, + the grants part's one source (ADR 0666 §20): `status`, `comment`, `view` and `note` the forge bridge alone, + `merge-pull` the persons and agent sessions the tool runner proves, the reads the parts that declare them. The forge module's own acting tools (`gitea_merge_pull_request`, + `gitea_commit_status`, `gitea_delivery_view`, `gitea_note_append`, `gitea_add_comment`) retire with the milestone, + and so do its watch of the checker's state `owed` and its read of the gate's `source`, so the bridge stays the one + part that acts on the forge. How it is checked: test 51 reads the grants, not the code. +- §314 **`merge-pull` judges no check, and from G's delivery no Approve, only the runs at the head's fingerprint.** The operator's answer that no check gates delivery (Telegram, 2026-11-09 01:11 UTC, + "Remove them in G") covers the forge module's own setting `check-gates-merge` as well as the gate's: both are + removed, and `merge-pull` merges an approved head whatever its `mesh/check` says. The record that ends ADR 0551 and + ADR 0548 §2 to §4, §9 and §10 also changes ADR 0534 §4's judgement of `mesh/check` by the merge; it is to be accepted + before the push (*Nothing merges into a repository under check*). From G's delivery it judges no Approve either, its caller + is any session the tool runner proves, and it refuses a head when any run at its fingerprint failed a `required` step, + never waiting for one (ADR 0666 §11; *Approval is a step, and a session merges a pull request*). The reads the + gate would have needed to merge (the forge's word for a conflict, the base's required statuses, the merging account's + write access) are not added: no part of the mesh but `merge-pull` merges, and it answers git's own refusal of a merge + as refused. +- §315 **The order is the module graph's, and each step has its way back.** The verbs are a served-verb edge (ADR 0614): + the forge module's new build runs before the forge bridge's, and the forge bridge's before the gate's, since the + bridge calls the verbs and performs the gate's `owed`, and the gate says `owed` only to a bridge that hears it; all + three are ahead of the acting parts' steps, which read through the seat. While the new forge runs and the old bridge + still calls the retired acting tools, the old bridge's acts fail loudly and stay unperformed in the old gate's keys, + and the switch carries them (*What the gate owes the forge*). A revert goes back in the other order: the gate, then + the bridge, then the forge, each wanted only once the one after it in the forward order runs its reverted build, so + no part calls a verb nobody serves. ADR 0656 §3 stands beside it: the acting parts' new versions act first in their + own steps. Two things cover the gap while the forge is still on its old build and serves no verb of the seat: the + indexer starts on its kept definitions (below), and a check whose read through the seat `git` fails says `could not + run`, naming the forge, and is asked again on the forge module's `running` (*A check runs to its end*). +- §316 **A part that cannot read the forge keeps what it has, and says so.** The indexer, at its start, waits for the + seat `git` at most 2 minutes; with no answer it judges from the last definitions it kept, says in its + `_first_views` that it waits for `git` and since when, writes `view-waits` after five minutes, and reads the + definitions once the seat answers; it then judges again every manifest it judged from kept definitions. Two minutes is longer than a forge's restart, and short enough that a start is + not held behind a forge down for good. How it is checked: test 54. +- §317 **How it is checked.** The seat definition's conformance fixtures hold one answer and one error of each verb and one + of each event, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's, the + indexer's and the gate's unit fakes answer from the same fixtures; a call of an act verb from an account other than + the forge bridge's is refused (test 51); live, a search of the delivery parts' and the gate's code finds no name of + the forge module's tools or events. + +§318 **Links of the trace.** No part reads a review line (ADR 0659 §1 to §3); who may review and the review verb's grant +stay issue 1782's, outside milestone G, and that verb gates nothing (ADR 0659 §2). The trace's links are this +design's choices under ADR 0656, so that one call traces research to the running build +with no text match (research 085, Findings 7). The issue tracker gains link kinds from an issue to the decision and to the research effort +it carries, made by a tool call and never by text, as ADR 0493 §4 has every link made; the knowledge base gains a +typed link from a decision to the milestone of the issue tracker that builds it, made by a tool call, which joins the +link kinds of its link-fits rule (design 55), beside the `became` link a research effort gets at its graduation +(playbook 02). How it is checked: whole (gate), one ask per milestone, withdrawn by a new head; +a waiter on one member's pull request wakes only on that member's transition. Whole (issue tracker, knowledge base): +an issue linked to a decision and to a research effort reads both back; a decision linked to its milestone reads it +back. Live: research 085's milestone's record reads back research 085 to its decisions, its milestone, its issues and +its pull requests with no text match. + +§319 **A session hears that a record was accepted** (issue 595). The knowledge base already says, on its holder's own +stream, the event `reviewed` for every judgement of a record; from milestone G that event carries, when the operator's +Approve accepts a record, that it was accepted and by the operator, with the warrant, and the tool runner's account is +granted to hear it, so a session waiting on a record wakes on it. The design's choice under ADR 0596 §1 (a module's +events are subscribed to): a new event of the seat `knowledge-base` would be a second path for the same fact (lead-47, +2026-11-09). How it is checked: in the knowledge base's whole-module test an Approve that accepts a record says +`reviewed` with what was accepted and by whom, and the tool runner's account hears it. + +## What retires at milestone G + +- §320 At research 085's milestone's delivery: the indexer's map from language to build module, and the checker's own + module names and its cases for a change to the manifest validator and to the Go build module (ADR 0656); a build + module's edge as a trigger, with the extra build per recipe change (ADR 0603 §3, as ADR 0656 changes it); composing + from manifests a module whose changed build module the check cannot run (ADR 0603 §10 and ADR 0614 §7, as ADR 0656 + changes them); the module graph's one key per module with five past values as what parts read (ADR 0603 §2, as + ADR 0656 changes it); the tiers worked out from five live sources (ADR 0603 §6, as ADR 0656 changes it); a seat + list compiled into a part, and every read of another module's bus state the revision view replaces (ADR 0656); the + gate's `merges`, carried into the delivery records and written no more, and its `owed`, said as events; the hold, + the merge turns, their stalled line and tools and the setting `check-gates-merge`, once their record is accepted; + the last three reads of a pull request's description (ADR 0659 §4); every delivery part's call of the forge module's + own tools, for the seat `git`'s verbs; and the four seat-list readers the delivery parts wire by hand. +- §321 At milestone G's delivery, from research 086 (ADR 0666, ADR 0667): the checker (`mesh-delivery-checker`), its seat, + its try with `tryevent` and the indexer's try candidates, its kept verdicts and carries (the runner's from then), the + checking program and `mesh/check`, and its buckets and bus account, removed with the gate's forge merge token only + after the first pipeline after G's delivery has delivered a change end to end and on the operator's one word, its + buckets through `drop` after a snapshot whose restore is tested (ADR 0667 §12); the bundle of steps run before a merge; the Approve per + merge, the milestone ask and its three events, and the gate's merges; the gate's merge states, one key per pull request and its verbs `table`, `recheck` and + `close`; the trunk-only want; `source.main`, for one source revision per watched branch; every fixed branch, + repository and module name in code logic; the recovery merge verb this design sketched, never built; and the holders' + trusted `callers` values, for the seat definitions' `callers`. The words **merge check**, **try**, **trunk commit** + and **merge turn** go to *Not:* lines with the as-is update after that delivery, as the glossary's entries say. + +## What milestone G's builders found open, answered by this design (2026-11-10) + +*ADR 0666, ADR 0667, ADR 0668; ADR 0656, ADR 0596, ADR 0595 §4, §9, ADR 0619 §1, §2, ADR 0563, ADR 0564 §2, ADR 0625 +§1, §4, ADR 0635 §4, ADR 0548 §5, ADR 0363 §2; design 27, *Which module answers*. The operator, 2026-11-10: a small +choice within what a record decides is the design's own, written here with its reason and reviewed by the reviewers +by architectural domain.* + +§322 Milestone G's prepare and develop met these questions (issue 1966). Each line is the design's choice, with the +record it follows from, and replaces the builder's own choice where one was made. + +### The runner and its steps + +- §323 **A step's build seat is read from the artifact's language.** The catalog graph entry names each artifact's + `language`, as its manifest's artifact entry says it and as the indexer's language map matches today; the runner + reads it from the catalog half of its revision view and asks lint and the test steps of the build seat whose + definition's acting rule `build` has `on` naming that language. With no such seat, or no holder of it, the pipeline + ends failed, naming the seat (ADR 0666 §18). Why: the language is already in the manifest and the catalog half is + the runner's one view (ADR 0666 §24, ADR 0596). How it is checked: whole (runner), a Go and a TypeScript fixture + module each ask their own build seat, and one with no holder ends failed naming the seat. +- §324 **An acting rule's `on` may name an artifact kind.** Beside `every` and a language, `on` may be `kind: image`, + matched against the manifest's artifact entry's kind; the seat `node-build-image`'s rule says `kind: image`. Why: an + image has no language, and the rule's fields are this design's (*What ADR 0656 leaves to this design*). How it is + checked: a unit row of the definition reader accepts `kind: image` and refuses another kind. +- §325 **An approval never asked ends `not asked`.** When a `required` step failed before the `approval` step was asked, + the `approval` step ends `not asked`, naming the failed step, never as passed; a `rerun` that passes the failed step + asks it then. `not asked` joins the outcomes of *Pipeline steps and the policy*. Why: approval comes last (above), + and every end a step can have is said as itself (ADR 0666 §10). How it is checked: whole (runner), a failed unit + step leaves the approval `not asked`, and its passing rerun asks it. +- §326 **A build seat keeps its `steps` answer.** The build module keeps each artifact's detected steps in its state + `steps`, from the template `state`, one key per artifact and build-input fingerprint, written at the first + detection and replaced by `steps` with `again`; the module deletes a key unread for 30 days at its next detection. + Why: ADR 0666 §25 asks a tool for "the steps detected for an artifact and detect again", which a reply computed + anew each time would not need; a build module is no foundation part, so the template form (ADR 0666 §4). How it is + checked: whole (Go build module), `steps` answers from the key, and `again` replaces it. +- §327 **The step code lives in the domain SDK.** The step run and the fetch-and-release code the three build modules + share move, in this milestone, into a build-step package of `mesh-delivery-sdk`, which the Go, TypeScript and image + build modules import; none keeps a copy. Why: code shared between modules lives in an SDK, and the delivery + domain's shared code is `mesh-delivery-sdk`'s (ADR 0635 §4); the copies were met in develop. How it is checked: a + search of the three build modules finds no fetch, release or step-run function of their own. +- §328 **Lint per language.** Go's lint step is the Go build module's linter with the fixed-names rule; TypeScript's is the + type-check with the fixed-names rule; an image's lint is the fixed-names rule over its build files; + an image has no unit or integration test step, so `steps` answers none for it and the seed has those `bypass` + (*A module's registration*). Why: ADR 0666 §5 has the lint step refuse fixed names in every language, and §7 lets a + build seat answer only the steps an artifact can run. How it is checked: whole (TypeScript and image build modules), + a fixture naming a module in code fails lint. +- §329 **What the fixed-names rule exempts.** It exempts a Go import path, a comment and a test file (`_test.go`, + `*.test.ts`), never a struct tag or any other string of code. Why: none of the three is code logic (ADR 0666 §5), + and a test fixture names modules. How it is checked: unit (lint rule), a name in a comment and in a test file + passes, in a struct tag fails. +- §330 **The runner's own kept data age by its own deletes.** The runner deletes its own `pipelines` keys, `kept` entries + and `step-outputs` objects by the retentions of *The runner's kept data*, at each step's end, for those past their + retention; a foundation part's sent store (`sent-objects`) is aged the same way by its owner at each send. Nothing + waits on a timer, and nothing but the owner deletes. Why: an old section cannot declare an age, and deleting one's + own entries is the owner's act, not the removal of a store ADR 0595 §4 holds to a person's word. How it is checked: + whole (runner), an output 7 days old is gone after the next step's end. +- §331 **The kept event streams' caps.** On the seat `mesh-pipeline-runner`, each kept 90 days: `pipeline-started` 32 MiB, + `pipeline-ended` 128 MiB, `group-ended` 64 MiB, `run-started` 16 MiB, `step-started` and `step-ended` 128 MiB each, + `wait-held` and `wait-ended` 32 MiB each; the seat `git`'s `branch-moved` 64 MiB, 90 days. Why: about 300 pipelines + a day of about 6 steps (*The runner's kept data*) say about 1,800 step events a day of about 0.5 KiB, about 80 MiB in + 90 days, and about 300 pipeline ends of about 4 KiB, about 105 MiB; estimates, read from the fill tools once G runs. + How it is checked: each seat definition's fixtures name each stream's `keep`. +- §332 **A group's end is said under the group.** `group-ended` is said on + `mesh.seat.mesh-pipeline-runner.event.group-ended..`, with no module part, since a group spans + modules. Why: every other runner event names one module (*Health and going back*). How it is checked: the seat's + fixtures. +- §333 **The runner hears the indexer's own event.** The runner's manifest `consumes` the indexer's own event + `take-in.taken` (ADR 0666 §13), which ADR 0596 §1 lets any module subscribe to; the indexer's name stands in that + manifest only, never in the runner's code. Why: ADR 0666 §13, in force, names that event. How it is checked: a search + of the runner's code finds no module name (lint). +- §334 **A publication's pipelines and their key.** `package-published` carries the package's artifact reference + (`:`, from the catalog entry), its version and digest, the watched branch and the source revision + its build was made from. The runner reads the dependents from that revision's catalog graph, the artifacts with a + kind-1 edge to the package, and starts each one's pipeline keyed by that branch, that source revision and the + dependent module, the version in the pipeline's state; a `take-in.taken` of the same revision and module is the + same pipeline (*A second start writes nothing*). Why: a pipeline's key is branch, source revision and module (above), + and the publication is made from a revision of a watched branch (ADR 0666 §15). How it is checked: whole (builder, + runner), a publish starts one pipeline per dependent, keyed by the publishing revision. +- §335 **What the teller says of a widening.** Each entry of `widens` names what widens (`acting-rule`, `grant`, + `credential` or `serving-part`), its name, the module and the commit. The teller says one message per + `pipeline-ended` that carries any, on every channel, once per module, watched branch and commit. Why: ADR 0666 §23 + says a widening in the pipeline's end, and the teller passes it on (*Who may say what*). How it is checked: whole + (teller), a `pipeline-ended` said twice with the same `widens` gives one message. + +### Callers + +- §336 **The forms of `callers`.** A verb's or an accepted kind's `callers` is a list whose entries are: a holder role (a + seat's name), a module, `operator`, `healers`, `persons` (any person the tool runner proves), `sessions` (an agent + session the tool runner proves), `invokers` (every module whose manifest declares the verb in `invokes`), and + `holder:@same-node` (the holder of that seat on the node of the verb's holder). A definition whose `callers` + today is text is given a list in this milestone: "any caller whose role grants it" becomes `invokers`, "a person" + `persons`, and every other text the names the design gives that verb. Why: ADR 0666 §20 makes the definition the + grants part's one source of caller limits, in a form it reads. How it is checked: unit (definition reader), each + form accepted and any other entry refused; the grants part's table test grants each form. +- §337 **The bus module's restore points.** The verbs the holder of `node-backup` on the bus module's node calls to take + and restore a module's kept data on the bus name `holder:node-backup@same-node`. Why: only that holder takes them + (*A backup before the apply: kept data on the bus*). How it is checked: a call from another node's `node-backup` + holder is refused by the bus. +- §338 **Callers on accepted kinds.** An accepted kind of a work queue carries `callers` as a verb does, and the grants part + grants publish on its subject to them alone: the build seats' `step` the runner; `run` the runner, the indexer and + the assigner, and the checker until it goes; `mesh-wants`'s `step` the runner. Why: a step's result is taken only + from its seat holder (ADR 0666 §21), so the ask side is limited too. How it is checked: a `step` published by another + account is refused by the bus. +- §339 **The contract generator reads `callers` and makes nothing of it.** The generated contracts carry no caller check; + the grants part reads `callers` from the definition, and the generator refuses a definition whose entry is no form + above. Why: one source of caller limits (ADR 0666 §20). How it is checked: the generator's test refuses an unknown + entry. +- §340 **The reader release first.** mesh-sdk's seat-definition reader released before G's check reads `callers` as text + or as a list; G's own reader refuses text. Why: readers first (ADR 0667 §4), and every definition is a list from G. + How it is checked: unit rows for both forms in the release, and for the refusal in G. +- §341 **TypeScript contracts as JavaScript.** The generator emits each seat's TypeScript contract as `index.js` with its + `index.d.ts`, so a TypeScript module imports it with no build step of its own. Why: a seat's contract is generated + from its definition (ADR 0625 §1), and a gap of the build system is built in it. How it is checked: a TypeScript + module builds against a generated contract with no `tsc` of the contract. + +### The catalog, the builder and the assigner + +- §342 **A module not watched on a branch.** A watched branch's catalog graph takes each module not watched on that branch + from the registration's first watched branch. `register` names a module's watched branches; with none named it takes + the repository's default branch, read through the seat `git`. Why: a placement's default is the registration's first + branch (ADR 0666 §6). How it is checked: whole (indexer), a `beta` revision holds a `main`-only module at `main`'s + head. +- §343 **Branch names the indexer's keys use.** `register` and `watch` refuse a watched branch named `candidate` or + `entry`, naming why, since the catalog keeps its revisions under `.` beside those two key forms. + How it is checked: whole (indexer), each is refused. +- §344 **The bus module plans a sent store from what a manifest already says.** It plans `_sent-objects` for a + module of the new form whose accepted manifest claims a seat whose definition has a verb or an event, or declares an + event of its own, read from the manifest and the seat list; the indexer's event carries no field `sends`. Why: no + manifest field (*Any size*). How it is checked: whole (bus module), a module claiming a seat with a verb gets a sent + store, one with neither gets none. +- §345 **A large value reaches the tool runner in chunks.** A bundle's put of a value over the bus's per-message limit is + sent to the tool runner in chunks under that limit, in order, each acknowledged, and written as one object through + the object store's own chunking; a put not sealed within the call's bound is dropped by the tool runner. No value is + split over objects. Why: one object per value (ADR 0666 §16), through the tool runner (ADR 0595 §9). How it is + checked: whole (mesh-sdk), a value of a hundred times the limit is read back whole. +- §346 **One object store package.** The assigner's and the indexer's object store code is mesh-sdk's small object store + package, with no copy in either. Why: ADR 0595 §9. How it is checked: a search finds no second copy. +- §347 **Reading sent objects is a bus state call.** mesh-sdk lists the call that reads a sent object under its existing + bus state capability. Why: ADR 0595 §9 puts the calls in that package. How it is checked: the package's table test. +- §348 **The builder reads the catalog through its seat.** The builder reads the registrations' watched branches through + `mesh-catalog`'s `registrations` and `registration-changed`, and a build seat's holder from the seat list; the + manifests, the packages, each package's name in its ecosystem (the Go module path, the npm name, which the catalog + graph entry's packages carry) and a run's candidate from the catalog half of its revision view, never the indexer's + state. With no holder of a build seat, a build ends `could not run`, naming the seat, and is asked again on the seat + list's change. Why: ADR 0596 §2. How it is checked: the builder's account holds no grant to the indexer's state. +- §349 **`package-published` is heard by the indexer.** The indexer keeps its package state from `package-published` for + every package, and from the builder's `version.numbered` only for images, bundles and archives. Why: the + publication is the seat's event (ADR 0666 §15). How it is checked: whole (indexer), a published package's version is + in its state. +- §350 **A superseded build's drop is a kept call.** The runner ends a superseded build's step `superseded` at once, and + asks the build seat's `drop` as a kept call (ADR 0668 §1, §2), so it is asked again through every transport fault + and said `call-unanswered` after five minutes. How it is checked: whole (runner), a drop unanswered is asked again. +- §351 **One rule for asking again.** mesh-sdk's kept-call form (ADR 0668 §7) asks an ask again only while it is open and + not replaced, and every asker and every hand ask-again tool uses it. How it is checked: unit (mesh-sdk), a replaced + ask is not asked again. +- §352 **The assigner's newer-revision check reads the want.** Each want names its source revision and watched branch, and + the assigner writes a want only by compare-and-set over a want of an older revision of that branch (*What holds a + delivery*). How it is checked: whole (assigner), an older revision ending after a newer one writes nothing. +- §353 **The assigner follows the registrations.** `mesh-delivery-contracts`' client `meshcatalogclient` carries a typed + follower of `registrations`, a first view then `registration-changed`, built on `go/views`; the assigner and the + forge module's merge switch on its first registrations (ADR 0667 §10). How it is checked: whole (assigner), it keeps + the trunk-only want until the first `registration-changed`. +- §354 **The assigner's composition step runs the composer in the sandbox.** It asks the seat `node-build-go` the ask `run` + (*What ADR 0656 leaves to this design*), its step keys under `assigner.`, and serves the ask's input by its seat + `mesh-wants`' verb `input`, kept a day in its state `inputs`, as the checker's seat does today; the indexer does the + same for its validation by `mesh-catalog`'s `input`. The Go build module reads no revision verb. Why: the + composition step runs on parts built from the heads, in the sandbox (ADR 0666 §17). How it is checked: whole + (assigner), a composition step's `run` is answered and its input read by digest. +- §355 **The assigner says an unanswered `manifest-reads`.** The assigner, the one caller of the node-engine's + `manifest-reads` from G, writes `call-unanswered` into its own findings state, and the condition keeper's reads gain + it in the same change (ADR 0668 §3). How it is checked: whole (assigner), a node-engine that never answers raises it + after five minutes. +- §356 **Two acting parts in different steps make no cycle.** The assigner's cycle check counts waits within one step only + (*A wait holds only within its step*). How it is checked: the unit rows there. +- §357 **Which provider a requirement points to.** In the assignment graph, by design 27's order: the named seat's + holder, a pin, the provider on the consumer's node, the only provider; otherwise the entry is held, naming the + candidates, and nothing is guessed. A contribution edge runs from a consumer's entry to the provider its requirement + resolves to, wherever that requirement carries values the consumer contributes. How it is checked: unit + (assignment graph), each step of the order and a hold. +- §358 **The catalog's shared view takes a branch.** The client's view of the catalog half is per watched branch, named by + its caller. How it is checked: unit (client), two branches give two views. +- §359 **`difference` names its revisions.** `difference` with none of `from`, `to` and `group` is refused, naming the + missing argument. How it is checked: a unit row. +- §360 **An approved ask binds the placement.** What an approved ask binds to includes the placement's watched branch and + its `build`, beside the fingerprint. How it is checked: whole (assigner), a placement changed after the Approve asks + again. +- §361 **A placement's `build` is a held put.** A placement's `build` is written as a held put of that build, released when + the placement drops it; a put a person wrote is never released by a placement change. How it is checked: whole + (assigner). +- §362 **The indexer's first-start catch-up runs apart from its source handler.** At its first start the indexer takes in + the heads the gate's source names by its catch-up path before it starts its handler of `source-moved`. How it is + checked: whole (indexer). +- §363 **A stale ask is dropped, backups included.** The assigner drops an ask it raised once the want it asked for is + replaced, a backup ask among them (built in mesh-delivery #200). Why: ADR 0596 and ADR 0668 §2. How it is checked: + whole (assigner). + +### The condition keeper and the node-engine + +- §364 **The node-engine claims its seat.** The node-engine's manifest claims the node seat `node-engine` (ADR 0563), and + the indexer and the node-engine's own guard read its holder from the seat list, keeping no module name. How it is + checked: lint finds no module name in either. +- §365 **The condition keeper reads its sources from the catalog.** The catalog graph entry carries each kept data a + manifest declares (its name and its section or template); the keeper takes a first view of the catalog half and + reads a module's `findings` wherever its entry declares a state `findings`, and the node-engine's holder from the + seat list. It joins the readers of *Every other reader of which modules exist*. Why: no fixed names (ADR 0666 §5), + and the keeper reads findings (ADR 0564 §2). How it is checked: whole (keeper), a fixture module declaring `findings` + is read with no code change. -- 2.54.0 From 63eeb96ebd181745fc0dca4881dadaf03925a336 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 20:32:13 +0100 Subject: [PATCH 02/13] Answer round 1 of the review of milestone G's part, and split the part so each file is read whole The four reviewers by architectural domain found retentions judged from another part's records, unbounded aging, waits and stores no condition names, a kept artifact checked against the builder's key, and caller limits left open; each is now a design line with its reason. The part is six files under 60 KB, one milestone still, with G.md as its index; the main file stays over its bound on the lead's word. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../G-1-pipelines-registration-and-steps.md | 399 ++++ .../G-2-pipelines-sizes-checks-and-tools.md | 343 +++ .../G-3-delivery-record-and-graphs.md | 376 +++ .../G-4-revision-view-and-steps.md | 354 +++ .../G-5-acting-parts-check-and-kept-data.md | 408 ++++ .../G-6-verbs-retires-and-answers.md | 478 ++++ 03-DESIGN/01-to-be/47-milestones/G.md | 2036 +---------------- 7 files changed, 2367 insertions(+), 2027 deletions(-) create mode 100644 03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md create mode 100644 03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md create mode 100644 03-DESIGN/01-to-be/47-milestones/G-3-delivery-record-and-graphs.md create mode 100644 03-DESIGN/01-to-be/47-milestones/G-4-revision-view-and-steps.md create mode 100644 03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md create mode 100644 03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md diff --git a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md new file mode 100644 index 00000000..12e31640 --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md @@ -0,0 +1,399 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-10 +decisions: + - "ADR 0668" + - "ADR 0667" + - "ADR 0666" + - "ADR 0664" + - "ADR 0661" + - "ADR 0656" + - "ADR 0633" + - "ADR 0619" + - "ADR 0614" + - "ADR 0596" + - "ADR 0595" + - "ADR 0593" + - "ADR 0584" + - "ADR 0573" + - "ADR 0567" + - "ADR 0564" + - "ADR 0548" + - "ADR 0448" + - "ADR 0429" + - "ADR 0398" + - "ADR 0363" + - "ADR 0283" +--- + +# 47, milestone G: pipelines per module: registration, watched branches, steps and policy, what holds a delivery, approval, groups, dependencies, a publish + +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". + +## Pipelines per module + +*ADR 0666, ADR 0667; ADR 0595, ADR 0596, ADR 0448 §2, §4, ADR 0548 §5, ADR 0564 §2, §4, ADR 0283, ADR 0656 §4, §6, ADR 0633 §5, +ADR 0664 §4, ADR 0398 §2, ADR 0593 §9; research 086, Direction.* + +§1 From milestone G's delivery. A change of any module, the delivery system's own parts included, goes from a commit to +the machines through that module's **pipeline**: the pipeline steps its **policy** names, run for each commit or group +on each of the module's **watched branches** by the seat `mesh-pipeline-runner`, ordered by the catalog graph's edges +between artifacts, with no hand act (ADR 0666 §1). Milestone G is the one milestone that builds it, beside the two +graphs, the revision view and the delivery record (ADR 0667 §1). Until G's delivery the sections this one replaces +(*Checks*, *A try*, *The Approve and the merge*, the state table and the trunk-only want) hold as written, and they +check and deliver milestone G itself (ADR 0667 §2); each of them says so in a line of its own. + +``` + a session ──merge-pull (seat git)──► forge ──commit on a watched branch──► forge bridge ──► indexer (mesh-catalog) + │ registrations; a catalog graph + │ per watched branch; the revision + │ validated before take-in + ▼ take-in.taken + pipeline runner (mesh-pipeline-runner) ── asks each pipeline step on the serving seat's work queue, hears step-ended: + │ lint, unit tests, integration tests ──► the build seats (node-build-go, node-build-node, node-build-image) + │ composition ──────────────────────────► the assigner (mesh-wants) + │ code review ──────────────────────────► the runner's own verb `review`, from the reviewer's bus account + │ approval ─────────────────────────────► operator-channel: accepted kind ask, then decided + ▼ pipeline-ended (every step's result), keyed by source revision + assigner ──► wants from a revision whose required steps passed, on the watched branch a placement chose + gate ──► the delivery record keeps the step and pipeline events +``` + +### A module's registration + +- §2 **What it holds.** The seat `mesh-catalog` keeps each module's **registration**: its git origin, the path of its + manifest, its watched branches and its policy, on the bus, every version with who changed it and the warrant of the + operator's Approve (ADR 0666 §2). The indexer is a foundation part, so the registrations are kept in its old sections + (ADR 0619 §1 to §3, ADR 0666 §4): one key per module in its old `state` section `registrations`, and every version on + its old `streams` section's stream `registration-history`, kept with no age limit. + The catalog graph is derived from the registrations and the manifests they point to; the mesh knows modules, and a + repository only as a module's git origin. +- §3 **Changing one is a trusted act.** The seat's verbs `register` (a module whole), `policy` (one module's step + statuses, per artifact), `watch` and `unwatch` (add or remove a watched branch) each raise an authorising ask of tier + approve through the seat `operator-channel` (design 46 §10), naming every value the change moves, one line each + ("`mesh-grants:program` unit tests: `required` → `bypass`"), and write the version only on its warrant; a Decline or + an expiry writes nothing (ADR 0666 §2). Until a record binds each forge account to its bus identity, `register` and + `policy` refuse code review as a `required` step, naming why, so no review gates while its author cannot be told + (ADR 0666 §22; issue 1966, note 157). A manifest moved to another path is the one manifest change that changes the + registration; any other manifest change is a commit, judged at its revision. +- §4 **Said and read.** Each written change is said as the seat's event `registration-changed` (the module, the version, + what moved); the verb `registrations` answers every registration, or one with its history. The indexer derives the + modules it keeps from them, and the forge bridge its watched branches, each by a first view of `registrations` and + then that event, never by reading the ledger (ADR 0666 §3, ADR 0596). +- §5 **An unprotected branch is refused.** `register` and `watch` read a branch's protection through the seat `git`'s read + verb `branch` (its head, and whether a rule refuses a direct push and admits only merges), and refuse a watched branch + the forge does not protect, naming it (ADR 0666 §3). +- §6 **The foundation parts are the manifest validator's list.** The manifest validator's list (ADR 0619 §3) holds + exactly the foundation parts, ADR 0619 §1's and the runner, which joins them (ADR 0666 §4); the checker leaves the + list when it is removed (*How milestone G itself is checked and delivered*). A registration holds no foundation + value. How it is checked: a unit test of the manifest validator refuses a template in the manifest of every part on + its list, the runner's included, and judges every other part's manifest as any part's. +- §7 **No part fixes a name.** Every branch, repository and module name a part needs is read from the registrations and the + catalog graph; the lint step refuses such a name in a string, a constant or a table of code logic, a Go import path + not being code logic (ADR 0666 §5). About 39 such names in 9 parts were found on 2026-11-09 (research 086, Findings); + milestone G removes them. +- §8 **The first registrations are one ask.** At its first start on G's build the indexer, holding no registration, + proposes one registration for every module its trunk revision holds, from what it took in (the origin and path of + each), its watched branch the repository's default branch read through the seat `git`, and the statuses the check + judged until then: lint, unit tests, integration tests and composition `required`, code review `optional`, approval + `bypass`. Each step the artifact's build seat answers `steps` with none is `bypass` instead, so no module is held by + a `required` step it has nothing for; the indexer asks `steps` of each build seat as a kept call (ADR 0668 §1, §2), + and the seed waits until every build seat has answered, never reading an unanswered seat as "none": a seat still + unanswered after five minutes is the indexer's `call-unanswered`, naming the seat (ADR 0668 §3), and the seat's new + build serving `steps` is an edge of kind 6 before the seed. A module whose default branch the forge does not protect + is left out of the seed and named in the ask, as `register` would refuse it (ADR 0666 §3). It raises the rest as one + ask naming every registration, as the seat `mesh-module-assigner`'s `adopt` proposes placement whole (ADR 0567). + While the ask is open the indexer's finding `seed-waits`, naming the ask, is raised by the condition keeper as + waiting on the operator (ADR 0283) and cleared by the warrant; a Decline or an expiry leaves the seed unwritten, says + so and keeps `seed-waits`, naming `indexer_seed_again`, which raises the ask again, recorded in the hand-act log. + Until that Approve the runner starts no delivering pipeline and says why, so the operator is told of this one ask + before G's delivery starts. Why: the design's choices under ADR 0666 §3, §7 and ADR 0668 (round 1 of the review by + architectural domain, 2026-11-10). How it is checked: whole (indexer), a build seat that never answers `steps` + holds the seed and raises `call-unanswered`, one that answers none gives `bypass`; an unprotected default branch is + left out and named; `seed-waits` is raised while the ask is open and cleared by the warrant. +- §9 **Kept for the life of the mesh**, never derived again (research 086, choice 25): the history stream's cap is 64 MiB. + About 120 registrations of about 1 KiB, and an estimated change a week each (a guess, not a measure), grow about 6 MiB + a year, so the cap holds about ten years; past 80 % the indexer says `filling`, as every store of *Two graphs, one + source of truth* does. +- §10 **How it is checked.** Whole (indexer): an unprotected watched branch is refused, naming it; a policy change without + the warrant writes nothing, and a declined one writes nothing; a change from `required` to `bypass` asks, showing + that line; `registration-changed` is said and `registrations` answers the history; the first start proposes the seed + as one ask, and `indexer_seed_again` asks it again after a Decline and after an expiry; a policy making code review + `required` is refused. Unit (the Go build module's lint step): a module name in a string, a constant and a table is + refused, an import path is not. Tests 75, 68 and 83. + +### Watched branches and the catalog graph per branch + +- §11 **Only a watched branch starts a pipeline.** The forge bridge hears every commit of every watched branch the + registrations name, through the seat `git`'s event `branch-moved` (a branch's new head, from the forge's push + webhook, which a merge and an admitted push alike say), and takes the commit from the forge's own record by the seat's + verb `branch`, never from the event's body (ADR 0666 §6). One event for every way a branch moves, because a merge is + not the only one. `branch-moved` is kept on the seat's stream `SEAT_git_branch-moved`, which the bridge hears through + a durable consumer, so a move said while the bridge is down is heard when it starts; at its start it also reads each + watched branch's head the same way. +- §12 **A head with no pipeline is a finding.** A watched branch's head that starts no pipeline within five minutes is the + watchman's finding `pipeline-stands`, naming the branch and its head, raised by the condition keeper (ADR 0564 §2), + unless it waits as progress not owed: a member of a gathered group is governed by ADR 0633 §5's 30-minute bound, and + a merge behind a pending revision of the same branch by its 60-minute bound (ADR 0666 §24). The finding clears on the + runner's `pipeline-started` for that head. +- §13 **The catalog graph per watched branch.** The indexer keeps the catalog graph once per watched branch, from that + branch's manifests at each source revision (ADR 0666 §6): each revision names its branch, and the stores, caps and + pins of *Two graphs, one source of truth* hold per branch. +- §14 **A placement names the branch.** Each module of a placement key names the watched branch its node runs, by default the + registration's first, set by a placement proposal like every other placement change (ADR 0567, ADR 0666 §6). The + assigner computes each node's entries from the catalog graph of the branch its placement names, so a module watched on + `main` and `beta` runs `beta` only where a placement chose it. +- §15 **A source revision per watched branch.** The gate keeps one source revision per watched branch, `source.`, + in place of `source.main` (ADR 0429 §11 to §13 as ADR 0666 §6 changes them), in the old `state` section `source` + already holds, its key's layout being the only change. The gate is no foundation part: the bus module's delivery + waits on no act of the gate's, since the indexer takes a revision in from the forge bridge's stream and the assigner + writes wants from the runner's events, so the gate's other kept data stay in ADR 0595's templates (ADR 0666 §4), and serves it by the seat + `mesh-delivery`'s verb `source` with the branch, saying `source-moved` on each new revision; the indexer and the + node-engine's catch-up read it there, never the gate's state (ADR 0596). Each want names a build-input fingerprint + computed at one such revision. +- §16 **Validated before any take-in, on every branch.** Before it takes a revision in, on every watched branch, the running + indexer asks the manifest validator built from the revision's heads to judge it in the build seat's sandbox (the + ask `run`, *What ADR 0656 leaves to this design*). A revision it refuses stays pending under ADR 0633 §5's + 60-minute bound, its hand acts and its condition `take-in-waits`; the way back is a revert, an ordinary merge + (ADR 0666 §6, §26). A revision on the branch the indexer's own placement chose is taken in only by the indexer it + names (ADR 0633 §1). This validation is no pipeline step and waits on no pipeline, so every delivering pipeline runs + on a revision already taken in. +- §17 **A branch whose protection was removed starts nothing.** On every `branch-moved` the forge bridge reads the + branch's protection again; a move of a watched branch whose protection was removed starts no delivering pipeline, and + the forge bridge writes the finding `branch-unprotected`, naming the branch, which the condition keeper raises and + clears once the protection is back (ADR 0666 §6). +- §18 **A pending revision holds its own branch only.** A merge waits behind a pending revision only on the same watched + branch; other branches move on (ADR 0666 §6, which changes ADR 0633 §3). +- §19 **A refused new indexer holds its branch until a revert lands.** A merge of a new indexer whose own validator refuses + its revision has already landed when it is refused, since the validation runs before take-in and after the merge + (ADR 0666 §6). It stays pending, and every later merge on that watched branch waits behind it, said by + `take-in-waits` at the 60-minute bound, until a revert, an ordinary merge through its pipeline (ADR 0666 §26), lands + and is taken in; nothing takes a later merge in past it. This is the cost of validating every revision with the + validator built from its own heads, the design's reading of ADR 0666 §6, and the operator is told it with the + condition, which names the pending revision and the revert as the way on. +- §20 **How it is checked.** Whole (forge bridge): a commit of a branch no registration names starts nothing; a commit on a + watched branch is taken from the seat's `branch` read, not the event; a head missed while the bridge was stopped is + read at its start. Whole (indexer, assigner): a commit on `beta` gives a revision of the `beta` graph only and moves + only the want of the node whose placement chose `beta`; a revision whose own validator refuses it stays pending and + holds `beta` while `main` moves on. Whole (watchman): a head with no pipeline for five minutes raises + `pipeline-stands`, cleared by `pipeline-started` for that head, and a gathered member does not; a move said while the + bridge was stopped is heard from the seat's stream. Tests 64, 68, 76, 78 and 82. + +### Pipeline steps and the policy + +- §21 **The runner.** The seat `mesh-pipeline-runner`, held once for the mesh by the module `mesh-delivery-pipeline`, + runs each pipeline (ADR 0666 §7). It is a foundation part, since the bus module's own delivery waits on its pipeline + (ADR 0666 §4): its kept data are in old sections (ADR 0619 §2), it waits on no bus module process to start, and it + joins the manifest validator's list of foundation parts, which refuses its manifest asking a template (ADR 0666 §4, + which changes ADR 0619 §1 and §3). It reads the policy from the registrations, asks each step that is not `bypass` of + the seat that serves it, and says each step's start and end and the pipeline's end (*Health and going back*, below). +- §22 **The steps and who serves them.** Lint, unit tests and integration tests are the build seats' (`node-build-go`, + `node-build-node`, `node-build-image`): each gains the verb `steps`, which answers which pipeline steps an artifact + can run by its language's convention (as *The check from release 4* says the tests are found), and the accepted kind + `step` on its stream `check`, which runs one. Composition is the assigner's, an accepted kind `step` of the seat + `mesh-wants`. Code review comes in through the runner's own verb `review`. Approval is asked as the seat + `operator-channel`'s accepted kind `ask`, on its work queue by the runner's name (design 46 §10, *Asks, warrants and answers on the bus*; + the seat has no verb `ask`), and heard on its event `decided` (ADR 0666 §7, §10). A step with nothing to do ends at once as + `nothing to run`, never as passed. +- §23 **The policy.** Each step's status, per module and per artifact, is `required` (runs; holds the delivery until it + passes), `optional` (runs; a failure is shown and may be accepted) or `bypass` (nothing runs; said as bypassed), + kept in the registration and never in a manifest: the manifest validator refuses a manifest that names a status + (ADR 0666 §7; research 086, R3). A `required` step that ends `nothing to run` holds the delivery and names the step. +- §24 **Asked on a work queue, ended by an event.** The runner asks each step on the serving seat's work queue and hears + its end as that seat's event `step-ended` (the record's `step ended`), on + `mesh.seat..event.step-ended.`, the node that ran it in the subject and each holder's account granted only + its own node's subject, through a durable consumer of its own, so a step outlives a restart of either side (ADR 0666 + §7, ADR 0363 §2). A build seat's holder restarted mid-step gets the step again from its work queue and finishes it + (ADR 0664 §1). A unit or integration test step says its test counts in `step-ended`, and `pipeline-ended` names any + repository over 50 integration or 500 unit tests, and any catalog module over them (ADR 0666 §8; the test budget). A step's outcome is one of `passed`, `failed`, `nothing to run`, + `bypassed`, `carried` (with the pipeline it was carried from), `could not run` (naming the part not heard), + `waiting on `, `accepted` (a failed `optional` step a person accepted, with who and why), `not asked` (an + `approval` step after a failed `required` step), `superseded` and `cancelled`. +- §25 **Carried, not run.** A step whose build-input fingerprint and policy did not change is carried by its package + digests, as ADR 0548 §5 holds, the runner making the carry instead of the checker: a test step runs only the packages + with no kept result for their digest, and a run on request's results are reused when the merged commit's inputs match + (ADR 0666 §8; research 086, choice 24). An `approval` step is never carried. +- §26 **Approval comes last.** The runner asks the `approval` step only once every other `required` step of the pipeline + passed, so the operator approves a commit the mesh already judged, the one place ADR 0661 §3 had the check gate the + ask. A failure after an approval is said, as a failed `optional` step is, and the approval stays. +- §27 **Kept on the bus, in old sections.** The runner keeps each pipeline's state in its old `state` section `pipelines`, + one key per pipeline keyed by its watched branch, source revision and module, written by compare-and-set so a holder + replaced mid-pipeline and its successor never both write one; its step log on its old `streams` section's stream + `step-log`; each step's output in its old `objects` section `step-outputs`, named in the log; and each package's + passing result per level in its old `state` section `kept`, under the digest of what its tests read (ADR 0666 §4, §8; + *The runner's kept data*, below). `kept` starts empty: the runner takes no verdict of the checker's, since a step's + result is taken only from the bus account of that step's seat holder (ADR 0666 §21), so the first pipelines after G + run each package's tests once and carry from then on (the design's choice, 2026-11-10). +- §28 **How it is checked.** Whole (runner): a step with unchanged fingerprint and policy is carried and says from where, + and an `approval` step is not; a module with no integration tests shows `nothing to run`, and a `required` unit-test + step on a module with no tests holds its delivery; a bypassed step is said; a runner restarted mid-step hears the + step's end and finishes the pipeline; two runners writing one pipeline key, one loses by compare-and-set; a test step's + counts are in `step-ended` and a repository over budget is named in `pipeline-ended`. Whole (Go build module): `steps` + answers the steps of a fixture artifact, a `step` ask ends with `step-ended` on its own node's subject, one on another + node's subject is refused by the bus, and a build module restarted mid-step finishes it. Unit (manifest validator): a manifest naming a status is refused. Tests 65, 66, 73 + and 88. + +### What holds a delivery + +- §29 **The assigner writes a want only from a passed pipeline.** The runner says each pipeline's end as `pipeline-ended` + (the record's `pipeline ended`), keyed by source revision, with every step's result. The assigner consumes it, and + writes a want only from a source revision whose every `required` step passed for the modules it moves, on the watched + branch a placement chose, by compare-and-set on a newer revision, so an older revision ending after a newer one + writes nothing (ADR 0666 §9). The compare-and-set is keyed per module and watched branch: it compares a revision only + with the last one written for the same module on the same watched branch, so a revision of `beta` ending after a + newer one of `main` writes its own want and refuses neither; a later `pipeline-ended` of the same revision (after a + passing `rerun`, below) is taken, since its end count is higher (the design's choice under ADR 0666 §9: source + revisions are kept per watched branch, `source.`, and two branches' revisions have no order between them). This is one more rule beside the assigner's others (ADR 0429 §6), checked per module. + From G's delivery it replaces the trunk-only want (ADR 0656 §2 as ADR 0666 §9 changes it). For a group's source + revision the runner says one `group-ended` once every member's modules have ended, and the assigner writes the + group's wants only on it: a member's own `pipeline-ended` writes nothing (ADR 0666 §9). +- §30 **A failed `optional` step holds nothing.** It is shown in `pipeline-ended`, in the delivery record and in the runner's + `show`; a person may accept it by the runner's verb `accept`, recorded in the hand-act log with who and why. +- §31 **A newer commit supersedes an older pipeline of a module whose fingerprint it changed.** On a newer commit of the + same watched branch the runner ends a module's older running pipeline as `superseded` only when the newer commit + changed that module's build-input fingerprint (ADR 0666 §9); a module the commit leaves as it was finishes its + pipeline. A newer publish of a package likewise supersedes the rebuild pipelines an older publish of it started. A + superseded pipeline's waiting asks are dropped by each build seat's `drop` (ADR 0664 §4), naming + the newer commit as the cause, so a cancelled pipeline leaves no ask on a stream (a gap G's prepare found: a cancelled + build withdrew only the builder's wait). +- §32 **A step nothing starts ends its pipeline.** A step whose prerequisite nothing starts (a seat with no holder, a + prerequisite step `bypass`) ends the pipeline refused within five minutes, naming the step and what it waits for, + never as a silent wait (research 086, R5); a step whose seat has no holder ends its pipeline failed, naming the seat + (ADR 0666 §17). +- §33 **A failed head is said to its author, then to the operator.** A `required` step that failed, or ended `could not + run`, on a watched branch's head is the runner's finding `pipeline-failed`, naming the module, the branch, the commit + and the step; the condition keeper raises it as the commit's author's for two hours and then the operator's, as ADR + 0505 §5 and §6 have a delivery whose cure is its author's new head, and clears it when a newer revision of that branch + passes that step (ADR 0666 §24). The author is the forge's record of the commit, read through the seat `git`. +- §34 **The delivery record keeps every step.** The gate's delivery record consumes the step and pipeline events and keeps + them as long as the record (ADR 0666 §9; *The delivery record*). +- §35 **How it is checked.** Whole (assigner): a failed `required` step writes no want; a failed `optional` step holds + nothing; an older revision's `pipeline-ended` after a newer one's writes nothing, and a revision of one watched branch + ending after a newer one of another writes its own want; a group with one member passed and + another still running writes no want until `group-ended`. Whole (runner): a newer commit that changed a module's + fingerprint supersedes its older pipeline and drops its asks, and one that left it unchanged lets it finish; a step + with no holder ends the pipeline naming the seat within five minutes; a failed `required` step on a head raises + `pipeline-failed`, the author's for two hours and then the operator's, cleared by a newer passing revision. Tests 66, + 67, 87, 89 and 98. + +### Approval is a step, and a session merges a pull request + +- §36 **No Approve per merge.** The operator's approval is the pipeline step `approval`, its status in each module's policy; + no Approve is asked per merge, and the delivery gate's merge turns and its Approve per merge go (ADR 0666 §10, which + changes ADR 0661 §3 to §5). The operator is asked only where a module's policy says `approval`, and for registrations + and policy changes. +- §37 **The approval is an ask of the runner's.** The runner raises an authorising ask of tier approve as the seat + `operator-channel`'s accepted kind `ask`, under its own name, its `about` the module and the watched branch only, so a newer commit's ask + replaces the older one's (design 46 §10). The warrant ends the step passed; an ask that is declined, expires or is + refused ends it `declined`, and one that is replaced ends it `superseded`. Only a declined `approval` step is asked + again, by the runner's verb `ask-again` (ADR 0666 §10). While it waits the runner + says `wait-held` waiting on the operator, never a stall (ADR 0283). +- §38 **A session merges a pull request.** Any session the tool runner proves, and the operator, merges by calling the seat + `git`'s verb `merge-pull` over the mesh, which merges with the forge tools' account (ADR 0398 §2) at the head the + caller names. It judges that this is still the pull request's head and that the branch's protection refuses a direct + push, the protection read again on every `branch-moved` of the branch, and it judges the head's runs by its modules' + policies: it refuses a head when any run at its build-input fingerprint failed a step a policy marks `required`, and + merges a head whose `required` steps passed or that has no run yet, a failed `optional` or a `bypass` step refusing + nothing; it never waits for a run and never asks an Approve (ADR 0666 §11). The forge keeps only its branch protection. +- §39 **Judged from its own state, never by a call.** The holder of the seat `git` consumes the runner's `step-ended` + through a durable consumer of its own and keeps, in its own state `failed-steps`, each failed `required` step by + module and build-input fingerprint, with the commits it was seen at and the run that failed it, until none of those + commits is a watched branch's head or the head an open pull request names (*The runner's kept data*); so a head + merges while the runner is down, judged on what it already heard (ADR 0666 §11, ADR 0596). A head's fingerprints are + those its runs named for its commit; a step that failed at that fingerprint in any run, a group's or another commit's + of the same content included, refuses it, unless a later run of that same step at that fingerprint, asked again by + the operator or the healers (the runner's `rerun`), passed: that pass clears the failure from `failed-steps` (ADR 0666 + §11). The runner's `step-ended` says that the step was a rerun and the caller the runtime proved, and the forge module + clears a failure only for a rerun whose caller is the operator or a healer; a plain pass, or a rerun asked by anyone + else, clears nothing. A run still running is judged by the steps it has ended, since the merge never waits. A refusal names the run, the module and the step, so the session knows what to fix. The gate merges nothing, and its forge merge token is + removed with the checker's buckets and bus account, after the first pipeline after G's delivery has delivered a change + end to end and on the operator's word (ADR 0667 §12; *How milestone G itself is checked and delivered*); the forge bridge hears the landed commit as it hears every commit of a watched + branch. +- §40 **The operator-channel hides no ask.** It keeps design 46's three open asks per asker, never holds the newest back, + and replaces an open ask by the same asker's ask with the same `about` (ADR 0666 §12; design 46 §6). +- §41 **How it is checked.** Whole (forge module, forge bridge): a session's `merge-pull` at its head merges with no run yet, + and with a passed one, and asks no Approve; with the runner stopped a head merges; a head at whose fingerprint any + run failed a `required` step, a head that moved and a branch whose protection was removed since its last move are + refused, each naming why; a failed `optional` step refuses nothing. Whole (runner): an `approval` step is asked last, + a Decline and an expiry each end it `declined` and `ask-again` asks it once more, and a newer commit's ask replaces the + older and ends its step `superseded`. + Live: a declined approval and a fourth ask each reach the operator. Tests 71 and 91. + +### Groups + +- §42 **Formed from the branch name.** The gate forms a group from a shared branch name carrying its issue number, as it + does today without the fixed list of trunk names (ADR 0666 §13). +- §43 **A member reaches its group by landing.** A member has reached its group when its commit lands on its modules' + watched branch. The forge bridge sets the group on each landed member's commit, reading the members from the seat + `mesh-delivery`'s verb `groups`, and publishes `forge.group.merged` when that verb says every member has (ADR 0666 §13, + which changes ADR 0633 §4). +- §44 **One source revision, one pipeline per branch.** The indexer keeps landed members pending until the group's end and + takes them in together as one source revision of that watched branch, saying `take-in.taken`; the runner starts the + group's one pipeline per watched branch on that event, and a member of a gathered group starts no pipeline of its own + (ADR 0666 §13). + When one member's `required` step fails, none of the group's modules is delivered (research 086, R5). +- §45 **A group that never completes is said.** By ADR 0633 §5's 30-minute bound and its condition; `bridge_group_end_again` + says a group's end again from the gate's group verb, never the gate's `merges` state (ADR 0666 §13, which changes ADR + 0633 §5). +- §46 **How it is checked.** Whole (indexer, gate, forge bridge): a group of three repositories, its own packages included, + is one source revision and one pipeline; one member's failed `required` step delivers none; a group with a member that + never lands raises `take-in-waits` at 30 minutes. Tests 67 and 81. + +### Dependencies between artifacts + +- §47 **Six kinds of edge between artifact references.** The catalog graph's edges run between **artifact references**, + `:`, such as `mesh-grants:program`, of six kinds (ADR 0666 §14): + + | Kind | B before A | Derived from | + |---|---|---| + | 1 | B published before A builds | a package A's build source imports (ADR 0573 §3) | + | 2 | B published before A delivers | an artifact A's manifest fetches by reference at its start | + | 3 | B delivered before A builds | a build module A is built by, and the toolchain its agreed version names (ADR 0584 §1) | + | 4 | B delivered before A delivers | a provision A requires and B provides, bus state among them (ADR 0614 §5), and a format's reader (below) | + | 5 | B has acted for A's new version | a seat's acting rule (*What ADR 0656 leaves to this design*): a grant, a credential, a verdict, bus state | + | 6 | B runs a version that serves A | a verb or a manifest word A uses and B serves or reads (ADR 0614 §2, §4), the build seats' `kept-artifact` among them | + + Each kind is derived from what manifests and seat definitions already say, and declared per artifact in the manifest's + artifact entry, its field `needs`, only where nothing else says it. Today's edges run between modules, and only a + package edge names an artifact; at module level the tool runner and the grants part looked like a cycle, per artifact + they are a chain (`mesh-grants:program`, then `node-tools:runtime`, then `mesh-grants:tools`). +- §48 **Removal runs the other way.** When something goes away the order is reversed: a seat moved to a new holder has its + old holder released before the new one claims (ADR 0666 §14). +- §49 **The edges alone order.** The build and the delivery order by these edges alone; a cycle of delivery edges (kinds 2, + 4, 5, 6) is delivered as one step, and a cycle of build edges (kinds 1 and 3) is refused, naming every artifact on it + (ADR 0666 §14; research 086, R17). The cycle rule is the indexer's, in the catalog graph it keeps, so the checking + program's cycle rule goes with the checker. +- §50 **A format's reader first.** A format a running reader cannot read is preceded by its reader, an edge of kind 4 on + every watched branch (ADR 0666 §14): the indexer's manifest validator for a new field of a seat definition or a + manifest, and on each node the node-engine that reads a manifest's new fields before any manifest that writes them + (ADR 0667 §4). +- §51 **How it is checked.** Unit (indexer's graph): a chain per artifact where modules would cycle; two programs needing + each other's new verb are one step; two artifacts each needing the other's new package to build are refused, naming + both; a seat moved to a new holder releases the old first. Live: the tool runner and the grants part delivered in + the order `mesh-grants:program`, `node-tools:runtime`, `mesh-grants:tools`. Test 79. + +### A publish rebuilds + +- §52 **Numbered at publication.** The builder numbers a package's version when it is published, never only when a build + is claimed, and says the publication as the event `package-published` of its seat `mesh-builds` (ADR 0666 §15; design + 51, *The versions*). The seat is new, held once for the mesh, defined in `mesh-delivery-contracts`; the builder held + none until now, and its waits stay on its own stream in this milestone. +- §53 **Every dependent is rebuilt against it.** On `package-published` every artifact with a kind-1 edge to the package is + rebuilt against the new version, and the runner, which consumes the event, starts its pipeline; nothing else is + rebuilt, and a change to tests alone publishes nothing (ADR 0666 §15; research 086, R18). +- §54 **A second start writes nothing.** The runner starts a pipeline on two events, the indexer's `take-in.taken` (ADR + 0666 §13) and `package-published` (ADR 0666 §15). When both reach it for the same source revision and module, the + second start writes nothing: the runner writes a pipeline's key (watched branch, source revision and module, *The + runner's kept data*) by compare-and-set on its absence, so one revision and module has one pipeline. The design's + choice under ADR 0666 §13 and §15: the key the runner already keeps is the one place a start is judged, so neither + event's producer needs to know of the other. +- §55 **Unread packages refuse.** A build that cannot read the published packages refuses and names the unread state + (ADR 0666 §15). +- §56 **How it is checked.** Whole (builder, indexer): a mesh-sdk merge changing one package publishes one version, and only + the artifacts importing that package rebuild; a merge changing only tests rebuilds nothing; a build whose packages + cannot be read refuses naming the state. Whole (runner): a `take-in.taken` and a `package-published` for the same + source revision and module start one pipeline, the second writing nothing. Tests 70, 80 and 101. diff --git a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md new file mode 100644 index 00000000..e8f80277 --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md @@ -0,0 +1,343 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-10 +decisions: + - "ADR 0667" + - "ADR 0666" + - "ADR 0664" + - "ADR 0661" + - "ADR 0659" + - "ADR 0656" + - "ADR 0644" + - "ADR 0619" + - "ADR 0596" + - "ADR 0595" + - "ADR 0593" + - "ADR 0573" + - "ADR 0567" + - "ADR 0564" + - "ADR 0548" + - "ADR 0525" + - "ADR 0505" + - "ADR 0448" + - "ADR 0363" + - "ADR 0283" + - "ADR 0240" +--- + +# 47, milestone G: pipelines per module: any size, the pipeline makes the check, who may say what, health, recovery, tools, kept data, how G itself is delivered, the choices under ADR 0666 and ADR 0667 + +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". + +### Any size + +- §57 **A value over the limit is an object.** mesh-sdk's own small object store package (ADR 0595 §9) puts a value over + the bus's per-value limit in the object store beside its state and keeps the object's name in the key; a verb's reply or an event over the + per-message limit carries the object's name (ADR 0666 §16). A replace writes the new object and sets the key by + compare-and-set; the object it replaced stays, as one whose key was deleted does, listed as `key gone` since no key + names it, and only the drop tool removes either, on a person's word: the mesh removes kept data only through `drop` + and `recreate`, each on a person's word (ADR 0595 §4; ADR 0666 §16). +- §58 **One object store per state, granted to its owner.** Each state asked by the template `state` gets its object store + beside it, `-objects`, a template form of ADR 0595 that the bus module provides and the grants part grants to + the state's owner alone (ADR 0666 §16); a foundation part's large values go in an old `objects` section of its own. + Its size is the template's (ADR 0595 §3). An object store holds no space until a value is written to it, so the many + it adds reserve nothing; the few values over the limit (a delivery record, a catalog graph revision list, a test's + output) are estimated, as a guess, at under 1 GiB across the mesh. +- §59 **An object sent is readable by the modules it is for.** A reply's or an event's object is written to the sender's + **sent store**, `_sent-objects`: the bus module plans it, with no manifest field, for every module of the new + form that serves a verb or emits an event, and a foundation part declares it as an old `objects` section + `sent-objects` (ADR 0619 §2). Its cap is the objects template's (ADR 0595 §3), and it keeps an object 7 days, after + which the bus expires it. That age is transport, not kept data: an object sent exists only to carry one reply or one + event, read within its call's bound or its consumer's lag, so its expiry is not a removal of kept data that ADR 0595 + §4 holds to a person's word (the design's choice; 7 days is a guess at the longest a consumer lags, read from the + fill tools once G runs). A full sent store is the bus module's `full` and its condition (ADR 0595 §6), and the sender + then refuses the large reply or event loudly, naming the store. The grants part derives from the manifests a standing grant to read them: the + callers its seat definition names for the verbs it serves, and the consumers whose `consumes` declare its events; + no other module may read them (ADR 0666 §16). The grant follows the manifests, so it is written when they change, + never per object. A consumer's reader of + the form is delivered before its producer writes it, an edge of kind 4. +- §60 **Its tools come with the package.** The package serves each module that imports it `_state_objects` (the + objects of each state, with their size, the key that names each or `key gone`, and the fill against its cap), + `_state_objects_drop` (drop the objects whose key is gone, on a person's word as ADR 0595 §4 removes kept + data), `_sent_objects` (each sent object, its size, when it was sent and to which verb or event, its age + against the store's and the fill against its cap) and `_sent_objects_drop` (drop sent objects older than an + age a person names); each drop names who asked and is recorded in the hand-act log. +- §61 **Its cost.** Only the modules that import the package are built with it, so no other module is rebuilt for it: a + package published before its users build (ADR 0573 §2, ADR 0667 §3). +- §62 **How it is checked.** Whole (mesh-sdk, against the real bus): values of one, ten and a hundred times the per-value + limit stored, sent as a reply and as an event, and read back whole by a caller and a consumer the manifests declare, + and refused to a module that declares neither; a replaced large value leaves its old object listed `key gone` beside + the new one, and a key deleted leaves its object listed `key gone`, each removed by the drop tool and by nothing else; + a sent object is readable within its 7 + days and expired after; each drop without who asked is refused. Tests 69 and 90. + +### The pipeline makes the check, and the checker goes + +- §63 **Every step runs on parts built from the change.** Every pipeline step that runs a change's code runs on parts built + from the change's own heads, in the build seat's sandbox, with no network and no bus account (ADR 0666 §17, ADR 0448 + §2, §4): the composition step composes each machine's declaration with the composer built from the heads and refuses + a change in which two modules own one path; the build seats' steps run lint and tests. A step whose serving part the + change alters runs from the change's build of it, built within 20 minutes or ending `waiting for a build node` or + `did not build`, the first tier of *The check on the change's own acting parts* made the runner's. +- §64 **The run on request.** The runner's verb `run` takes one or more members, each a module at a named commit, runs them + through their pipelines and delivers nothing, replacing the checker's try (ADR 0666 §18). Its callers are the + sessions, as the agents' bus identities the tool runner proves, and the operator, and nothing else, since a run of a + group builds and publishes its packages' tagged versions (design 51). The runner says `run-started`, the indexer builds + the run's candidate from it, and the candidate's composition step writes no want (ADR 0656 §9); the candidate is + dropped on that run's `pipeline-ended`. A step that could not run ends `could not run`, never as passed. A + composition step for a node that sleeps passes it with a warning (ADR 0593 §9). +- §65 **The runner is an acting part in the checker's place.** The acting rule's step `check` becomes the runner's step + `pipeline` (*What ADR 0656 leaves to this design*): its own change runs on its own new version, a failing new runner + holds while the old one runs pipelines (ADR 0656 §4), and the pipelines in flight when its holder is replaced are + taken up again from its kept state (ADR 0666 §19). +- §66 **What the checker did, and where it goes.** + + | The checker's | From G's delivery | + |---|---| + | the merge check, one status `mesh/check` | the pipeline, its result in `pipeline-ended`, the runner's `show` and the delivery record; no status on the forge | + | the try | the run on request | + | the composition check | the composition step, the assigner's | + | the kept verdicts and the carry | the runner's `kept`, which starts empty, and its carry (ADR 0548 §5); the checker's verdicts go with its buckets | + | the checking program: the manifest validator at the heads, the cycle rule, the composer, the rule names | the indexer's validation before take-in; the indexer's cycle rule; the composition step; `widens` in the pipeline's end | + | the `run` ask, published by the checker's account | published by the runner's account and the indexer's (its validation), each with step keys under its own name | + | its buckets and bus account | removed with the gate's forge merge token after the first pipeline after G's delivery has delivered a change end to end, on the operator's one word, the buckets through `drop` after its snapshot, whose restore is tested first (ADR 0667 §12) | + +- §67 **How it is checked.** Whole (runner, assigner): a run on request of `feature/1966-x` delivers nothing, and a call of + `run` from another account is refused; a composition step refuses two modules owning one path; a step whose part + cannot be heard ends `could not run`; a node asleep passes with a warning; a runner stopped mid-step takes its + pipelines up again. Live: once the checker is removed no module claims the seat `mesh-delivery-checker`. Tests 64, + 84 and 88. + +### Who may say what + +- §68 **Callers per verb, in the seat's definition.** Each seat definition names, per verb, the callers allowed to call it, + in its field `callers` (holder roles, modules, the operator, the healers), which the grants part reads as its one + source of caller limits; the runner's verbs are the first (ADR 0666 §20). A verb whose definition names no `callers` + admits only the seat's holder roles and the operator, so a field left out opens nothing. It replaces the holder's + trusted config value `callers` of the seat `git` and the package seats (design 51), which no holder set for most + verbs. A format its running reader cannot read, so its reader is delivered first (ADR 0667 §4). +- §69 **A step's result from its holder only.** The runner takes a step's result only from the bus account of that step's + seat holder, naming the commit and the build-input fingerprint; a result from another account, or naming another + commit or fingerprint, is refused and said (ADR 0666 §21). +- §70 **A review gates, once it can.** A code review's result is taken through the verb `review` from the reviewer's bus + account and refused from the commit's author, so a review gates where a policy says `required` (ADR 0666 §22, which + changes ADR 0659 §2). Until a record binds each forge account to its bus identity no policy may say it (*A module's + registration*; issue 1966, note 157). +- §71 **A widening is said, never refused.** A change that alters any part serving a step (the runner, step detection, the + lint rule, a build seat's test runner, the composer), or widens an acting rule, a grant or a credential, says so in + its `pipeline-ended`, its field `widens` naming each. The running runner computes it, from the sources of the change + against those of the watched branch, never the runner built from the change, so a change cannot hide its own + widening; nothing refuses it for that (ADR 0666 §23, the operator, 2026-11-10). The teller passes `widens` on to every + channel, as it does `recovery-merged`. This is what replaces ADR 0656 §8's naming in the Approve ask. +- §72 **How it is checked.** Whole (runner, grants part): a forged step result and an author's own review are refused; a + caller not in a verb's `callers`, and any caller but the holder roles and the operator of a verb with no `callers`, + are refused by the bus; a change widening a grant, and one altering the Go build module's test runner, each name it in + `widens`, computed by the running runner, and are delivered. Tests 85 and 86. + +### Health and going back + +- §73 **The runner's events.** Each on `mesh.seat.mesh-pipeline-runner.event....`, + a branch or group name escaped as a key's name is, declared in the seat's definition and published by the runner's + account alone (ADR 0666 §24, ADR 0363 §2): + + | Event | Says | + |---|---| + | `pipeline-started`, `pipeline-ended` | a pipeline's start, and its end with every step's result, its test counts, the source revision and `widens` | + | `group-ended` | a group's one pipeline's end, on which alone its wants are written | + | `run-started` | a run on request's start, with its members at their commits | + | `step-started`, `step-ended` | each step's start and end: the step, its status in the policy, its outcome, its build-input fingerprint, its test counts, for a carried result what it was carried from, and for a rerun that it is one and the caller the runtime proved; a bypassed step too | + | `wait-held`, `wait-ended` | a step's wait: on a person (approval, code review), on a build node, on the revision view | + +- §74 **What a session hears after its merge.** Between its `merge-pull` and the delivery, a session waits with `mesh_wait` + on the runner's `pipeline-started` and `pipeline-ended` of its module, branch and commit, and then on the status + view's `delivered` of the source revision; a failed head reaches it as `pipeline-failed` (*What holds a delivery*). +- §75 **Nothing stands still unseen.** The watchman times each step's owed progress from `wait-held` and `step-started`, + never by run length; a step or a watched branch's head standing still five minutes is its finding `pipeline-stands`, + raised by the condition keeper and cleared on the step's end, or for a head on its `pipeline-started` (ADR 0564 §2, + §4). A step waiting on a person is said as waiting on that person (ADR 0283), never as a stall; a group waiting for + its members, and a merge behind a pending revision, are governed by their own bounds (ADR 0666 §24). At its start the + watchman seeds its open waits from the runner's `stands`, as it seeds `take-in-waits` from `indexer_pending`. +- §76 **The runner's own health.** Its manifest declares its health check, run outside its process (ADR 0240): the runner + answers `show`, and its durable consumers of `step-ended`, `take-in.taken`, `package-published` and `decided` are + each less than five minutes behind; a failed check names which. +- §77 **The runner waits for its view.** With no revision view it says so in `_first_views` and raises `view-waits` + after five minutes, as every part does (ADR 0656 §6). +- §78 **Going back is one act.** A failing new version holds while the old keeps running (ADR 0656 §4); a return to an + earlier build is one value in the placement, the module's `build` in its placement key, set by a placement proposal + (ADR 0666 §24). +- §79 **How it is checked.** Whole (runner, watchman): a step whose worker dies raises `pipeline-stands` within five minutes; + a head with no pipeline raises it and its `pipeline-started` clears it; a step waiting on the operator raises nothing; + a runner restarted mid-step takes its pipelines up again; its health check fails naming a consumer behind. Whole + (assigner): a placement's `build` delivers the earlier build. Tests 76, 77 and 88. + +### A recovery fix is an ordinary merge + +- §80 **No recovery merge verb.** A fix that recovers the mesh is merged like any change on a watched branch, by a + session's `merge-pull`, and runs its pipeline; no trusted recovery merge verb is built (ADR 0666 §26). The shape this + design sketched for one (a verb `merge` of the seat `mesh-delivery` with `recovery-callers`) is dropped. +- §81 **Named a recovery, said everywhere.** Its author names it a recovery by the seat `mesh-delivery`'s verb `recovery` + (the commit and why), never by a pull request's text: the gate marks the delivery record `recovery` with the caller + the runtime proves, and says `recovery-merged`, which the teller passes on to every channel (ADR 0666 §26). A widening + it carries is said in its `pipeline-ended` like any other (*Who may say what*). +- §82 **How it is checked.** Whole (gate): a merge named a recovery is marked in its record and `recovery-merged` is said; + a recovery named by a description is not. Test 92. + +### The tools + +§83 Every new state has a tool that reads it and every new act a tool that starts it by hand (ADR 0666 §25), against the +four lines a module's tools answer; every read tool of a store also answers its fill against its cap: + +| Part (seat) | Read everything it holds | Do by hand what it does itself | Recover without help | +|---|---|---|---| +| the runner (`mesh-pipeline-runner`) | `show` (one pipeline whole, its steps, step log and outputs), `list` (the pipelines of a module, a branch or a commit), `stands` (what stands still, since when), `why-carried` (where a step's result was carried from), `kept` (the kept results by digest) | `start` (a delivering pipeline for a watched branch's head), `run` (the run on request), `rerun` (one step again) | `accept` (a failed `optional` step, with why), `cancel` (a pipeline, dropping its asks), `ask-again` (a declined `approval`) | +| the indexer (`mesh-catalog`) | `registrations` (with each one's history); `indexer_edges` (an artifact's edges per watched branch), `indexer_order` (the order of a change, its cycles named), `indexer_catalog_difference` (two revisions); `indexer_pending` (a group with its missing members) | `register`, `policy`, `watch`, `unwatch` (each on the operator's Approve) | `indexer_seed_again` (the first registrations' ask raised again), `indexer_take_in_pending` (a group taken in as gathered), `indexer_drop_pending` (dropped) | +| the gate (`mesh-delivery`) | `source` with a branch (`gate_source`: every branch's source revision) | — | `gate_source_again` (say `source-moved` again for a branch) | +| the forge module (`git`) | `git_failed_steps` (the failed `required` steps it holds, by module and fingerprint, with the run that failed each) | — | `git_failed_steps_drop` (drop one, with why) | +| the grants part (`mesh-grants`) | `grants_callers` (the callers each verb admits, read from the seat definitions) | — | `grants_why_refused` (for an account and a verb, the grant it lacks and the definition's line that leaves it out) | +| the build seats | `steps` (the steps detected for an artifact) | `steps` with `again` (detect again) | `drop` (ADR 0664 §4) | +| the builder (`mesh-builds`) | `builder_versions` (a package's published versions) | `builder_publish_again` | — | +| the assigner (`mesh-wants`) | `assigner_why_not` (why a module is not on a machine yet: the pipeline it waits for, its hold, the placement, the node's last `running`) | — | `assigner_release` | +| the watchman | `watchman_views` (how far behind each part's view is, from each part's `view-waits` and first-view tools) | — | each part's `_first_view_again` | +| the operator-channel | `asks` (with each asker's asks held back) | — | `ask-again` (offer a held-back ask now) | +| every module that imports mesh-sdk's small object store package | `_state_objects`, `_sent_objects` | — | `_state_objects_drop`, `_sent_objects_drop` | + +§84 Every recover tool writes its act, who and why in the hand-act log of the seat `mesh-hand-acts`. How it is checked: +each tool answers its whole-module test's case, every store's read answers its fill, and `mesh_describe` reads each +(test 93). + +### The runner's kept data, and the build capacity + +§85 Each kept form names its section or template, its retention and its cap; a state template fixes its own size (ADR 0595 +§3), up to 64 MiB. The numbers are estimates, to be read from the fill tools once G runs: an estimated 20 merges a day +(as *The delivery record*), each starting a pipeline for about 3 modules of its repository whose fingerprint it changes, +and an estimated 2 mesh-sdk publishes a day, each rebuilding about 120 artifacts and starting about 120 pipelines: about +300 pipelines a day, of about 6 steps each. + +| Part | Kept data | Section or template | Retention | Cap and growth | +|---|---|---|---|---| +| the runner | `pipelines`: one key per pipeline, keyed by watched branch, source revision and module, its steps, statuses, outcomes, fingerprints, test counts and end count | old `state` section | a pipeline that ended passed on a watched branch, 90 days after its end; any other (failed, superseded, cancelled, a run on request's), 7 days after its end; always while it is its branch's newest for its module; deleted by the runner (§330) | 256 MiB; about 300 pipelines a day of about 4 KiB is at most about 105 MiB at 90 days, under half the cap | +| the runner | `step-log`: one entry per step's start and end, its output, its pipeline and the `kept` digests it wrote or carried, by name | old `streams` section | by its age, 100 days, ten days past the longest retention it ages (§330) | 128 MiB; about 3,600 entries a day of about 0.2 KiB is about 70 MiB at 100 days | +| the runner | `step-outputs`: a lint or test step's output | old `objects` section | 7 days | 1 GiB; about 1,200 steps run a day (the rest carried) of about 50 KiB is about 420 MiB at 7 days | +| the runner | `kept`: a package's passing result per level, by digest, with `last-read`, written by each carry that reads it | old `state` section | 30 days after `last-read` | 128 MiB; a publish changes the digest of about 1,000 importing packages at two levels, so about 4,000 entries a day of about 0.3 KiB, about 36 MiB at 30 days | +| the indexer | `registrations` and its stream `registration-history` | old `state` and `streams` sections | the life of the mesh, never derived again | 64 MiB (*A module's registration*) | +| the indexer | catalog graph revisions, per watched branch | objects in the old form (ADR 0619 §2) | while a record pins it or it is its branch's current | 512 MiB, as *Two graphs, one source of truth*; a second watched branch on a few modules adds a few MiB | +| the gate | `source`: one key per watched branch | its old `state` section, as today | 64 revisions of each key; a branch's key deleted when `registration-changed` says the last registration watching it unwatched it (`unwatch`) | a few KiB | +| the forge module | `failed-steps`: failed `required` steps by module and fingerprint, with the commits each was seen at | state | an entry deleted once none of its commits is a watched branch's head or the head an open pull request names, judged on each `branch-moved` and each pull request's close | the template's 64 MiB; an estimated 50 failed steps a day of about 0.5 KiB, most gone within days, is under 1 MiB | +| every module that imports the object store package | `-objects` beside each state | objects | an object no key names, its key deleted or its value replaced, until `_state_objects_drop` on a person's word (ADR 0595 §4, ADR 0666 §16) | the template's; nothing reserved until written | +| every sender of the new form, and a foundation part by its old `objects` section | `_sent-objects` (`sent-objects`) | objects, planned by the bus module with no manifest field | 7 days, transport (*Any size*) | the objects template's cap; full is the bus module's `full` | +| the grants part, for each seat event a definition keeps | `SEAT__`, among them `SEAT_mesh-pipeline-runner_*`, `SEAT__step-ended`, `SEAT_git_branch-moved`, `SEAT_mesh-builds_package-published` and `SEAT_mesh-catalog_registration-changed` | a stream the grants part makes with its durable consumers | the seat definition's age (`keep`), 90 days unless the definition names less | the seat definition's cap (`keep`) | + +§86 **The build capacity.** A pipeline runs a module's steps once per commit on a watched branch, carrying what did not +change, where the check ran before the merge and carried after each trunk move; a group runs one pipeline per watched +branch; a second watched branch runs the steps of the modules it carries once more. A mesh-sdk publish asks about 120 +lint steps and the test steps of the importing packages, about 240 more, the rest carried; a newer publish of the same +package supersedes the older one's rebuild pipelines, so two publishes close together run the rebuilds once. The first +pipelines after G's delivery run every module's lint and tests once, since `kept` starts empty (ADR 0666 §21): about +120 modules, one round of the build capacity, taken in the `check` stream's order; from then they run only what +changed. Before G's delivery the operator is told the first round's estimated length, read from the checker's last +full check of every module (its verdicts' run times). A step queued on `check` behind the build capacity is said +`wait-held` on a build node and is owed no progress while queued; the watchman raises one finding per build seat, +`build-capacity-waits`, when that seat's oldest queued step has waited 60 minutes, cleared when it starts. A pipeline +whose commit is named a recovery has its steps asked on the build seats' stream `delivery`, ahead of the first round. The steps go on the build seats' stream `check`, +after `delivery` and before `recheck`, within each node's build capacity (ADR 0525 §4, §5), and a run on request on the +same stream. How it is checked: live, the build nodes' time per day in the week after G, read from the build modules' +step keys and the builder's build log, against the checker's last week (test 99). + +### How milestone G itself is checked and delivered + +- §87 **Under the clauses as they stand.** Milestone G is checked and delivered under ADR 0656 §5 and the clauses it changed + as they hold before its delivery: the acting parts (the indexer, the build modules, the assigner, the grants part, + the node-engine, and the checker, which makes G's own check) act from the milestone's heads, each first in its step, + in the order of their ordering edges; the trunk-only want, the checker and the Approve per merge as ADR 0661 leaves it + govern this delivery, no exemption is asked and no delivery goes ahead of it (ADR 0667 §2, ADR 0644 §1). +- §88 **Its other parts as any module.** mesh-sdk's small object store package is a package published before the modules + that use it build; the manifest validator moves with the indexer; the runner is placed like any module and acts only + from the next change on; mesh-delivery's `contracts` and `sdk` move under its `modules/` folder (ADR 0667 §3). +- §89 **Readers first** (ADR 0667 §4): an edge of kind 4 each, which the delivery plan derives: the indexer's manifest + validator for the seat definitions' new fields (`callers`, `acts`, `subject`, `keep`) before any definition using + them; on each node the node-engine that reads `form` and `catch-up` before any manifest writing them. +- §90 **The stream maker first** (ADR 0667 §5): the grants part, which makes each kept seat stream and its durable + consumers, before the parts that hear a kept stream. +- §91 **The bus module first** (ADR 0667 §6): the bus module, with `-objects`, the sent store and the grant of each + state's object store to its owner alone, before the modules requiring them, the grants part writing that grant from + the milestone's heads. +- §92 **A bus restart is said first** (ADR 0667 §7): a restart of the bus its new version needs is said to the operator + before it starts. +- §93 **A gap is built, not worked around** (ADR 0667 §8): an order the delivery plan cannot derive from the edges is + built in the delivery system within G, never a step done by hand. +- §94 **A node-engine of the version before applies what it can read** (ADR 0667 §9): it applies every want whose + manifests use no field it does not read, and a want that uses one waits, by the reader edge above, for the new + node-engine on that node. +- §95 **The new assigner and the new merge switch on the first registrations** (ADR 0667 §10). The new assigner, which + writes a want only from a passed pipeline, is delivered before the forge module's new `merge-pull`, which asks no + Approve, and before the runner: an edge of kind 4 from `mesh-delivery-assigner` to each, declared in their manifests' + `needs` since nothing else says it (the design's choice: without it a merge made with no Approve could reach the old + assigner, which writes wants from the trunk with no pipeline). Both switch on one fact, the first registrations + written, said as `registration-changed`: until it the new assigner keeps the trunk-only want and the new + `merge-pull` refuses every merge, naming the missing registrations. +- §96 **Until then, the operator merges a fix at the forge** (ADR 0667 §11). Until that fact an ordinary change waits for + it, and a fix, an indexer fix included, is merged by the operator's own hand at the forge (ADR 0661 §3) and + delivered under the trunk-only want still in force; `indexer_seed_again` asks the first registrations again. Before + the milestone's delivery every merge goes as ADR 0661 has it. +- §97 **What is removed, and when** (ADR 0667 §12). The checker's buckets and bus account and the gate's forge merge token + are removed only after the first pipeline after G's delivery has delivered a change end to end (test 63), the runner + then making every check in the checker's place (ADR 0666 §19), and only on the operator's word, asked once for all + three. The checker then leaves the manifest validator's list of foundation parts (ADR 0666 §4) and is no longer + placed, so no running module declares its buckets and they + are orphans `orphans` lists; they go through ADR 0595's `drop` on that word, after the stream snapshot `drop` takes + (ADR 0595 §4, §5, §10), kept 90 days as the delivery records are; a restore from that snapshot is tested before the + word is asked. The runner is on that list from G's delivery. +- §98 **Told to the operator before it starts**: G's own merges are asked as ADR 0661 has them (ADR 0667 §2, §11); the one + ask of the seed registrations; the bus's restart (ADR 0667 §7); and later the one word for the checker's removal + (ADR 0667 §12). +- §99 **How it is checked.** Tests 94 to 97, and 100 for the assigner's edge. + +### Choices this design makes under ADR 0666 and ADR 0667 + +| Choice | Why | +|---|---| +| §100 The registrations in the indexer's old `state` section with their history on an old stream, capped at 64 MiB, kept for the life of the mesh | the indexer is a foundation part (ADR 0619 §1 to §3, ADR 0666 §4); a registration keeps every version with who and on whose Approve, and nothing derives it again | +| §101 The runner a foundation part, its kept data in old sections, on the manifest validator's list | the bus module's own delivery waits on its pipeline (ADR 0666 §4) | +| §102 The want's compare-and-set keyed per module and watched branch | source revisions are kept per watched branch, so two branches' revisions have no order; comparing across them would let `main` refuse `beta`'s want (ADR 0666 §9) | +| §103 A second start of a pipeline for the same source revision and module, on `take-in.taken` and `package-published`, writes nothing, by the pipeline key's compare-and-set | the runner starts on both events (ADR 0666 §13, §15); the key it already keeps judges a start in one place, so neither producer knows of the other | +| §104 A refused new indexer revision holds later merges on its branch until a revert lands | the validation runs after the merge and before take-in (ADR 0666 §6), so the refused revision has landed; taking later merges in past it would deliver them on a graph its own validator refused | +| §105 Each check at its row's level, every requirement R1 to R18 keeping its named test | the record's rows name the levels; the test budget and one check per release bound what runs | +| §106 The approval asked as the operator-channel's accepted kind `ask` on its work queue | the seat has no verb `ask`: an ask is its accepted kind, by the asker's own name (design 46 §10) | +| §107 The gate no foundation part, its `source` in the old section it already has | the bus module's delivery waits on no act of the gate's; only the key's layout changes | +| §108 The verbs `register`, `policy`, `watch`, `unwatch`, `registrations` and the event `registration-changed` on `mesh-catalog` | one verb per trusted act, each its own ask naming what moves; readers follow the event (ADR 0596) | +| §109 The seed of the first registrations proposed as one ask, with the statuses the check judged until then and approval `bypass`, raised again by `indexer_seed_again` | registering is a trusted act (ADR 0666 §3), and one ask for the whole seed is how placement was adopted (ADR 0567); a declined or expired seed must not leave the pipelines stopped with no way on | +| §110 Code review refused as `required` until forge accounts are bound to bus identities | a review is refused from its author (ADR 0666 §22), which cannot be told until then | +| §111 The seat `git`'s read verb `branch` and event `branch-moved`, kept on its seat stream | registering reads a branch's protection through the seat `git` (ADR 0666 §3); a branch moves by a merge or an admitted push; a stream keeps a move said while the bridge is down | +| §112 The watchman's finding `pipeline-stands` for a step or a head still five minutes, cleared by the step's end or the head's `pipeline-started` | the record's five minutes (ADR 0666 §24), one finding for both, timed from the runner's events | +| §113 The runner's finding `pipeline-failed`, the author's for two hours, then the operator's | ADR 0666 §24 with ADR 0505 §5, §6 | +| §114 A module's watched branch and its `build` in the placement key, set by a proposal | a placement names the branch and a return is one value in it (ADR 0666 §6, §24); every placement change goes by a proposal (ADR 0567) | +| §115 The gate's `source.`, its verb `source` and event `source-moved` | one source revision per watched branch (ADR 0666 §6), read through the seat (G's prepare, the indexer's watch of the gate's `source`) | +| §116 The build seats' verb `steps` and accepted kind `step` on `check`; the assigner's kind `step` on `mesh-wants`; `step-ended` with the node in its subject | steps are asked on the serving seat's work queue (ADR 0666 §7); the `check` stream already orders after `delivery`; a node's account says only its own node's ends (ADR 0363 §2) | +| §117 The outcomes, with `accepted`, `superseded` and `cancelled`; an approval expired or refused ends `declined`, replaced `superseded` | every end a step can have is said as itself (ADR 0666 §10) | +| §118 `approval` asked last, its `about` the module and the branch | the operator approves a commit the mesh already judged, the point ADR 0661 §3 kept in one place; one ask per module and branch, a newer commit's replacing it | +| §119 A superseded pipeline's asks dropped by `drop`, and a pipeline superseded only when its module's fingerprint changed | a cancelled pipeline must leave no ask on a stream (ADR 0664 §4; G's prepare); a module the commit did not change has nothing newer to run (ADR 0666 §9) | +| §120 A group's wants only on `group-ended` | a group is delivered whole or not at all (ADR 0666 §9; research 086, R5) | +| §121 The six kinds derived from existing words, `needs` per artifact only where nothing says it | one form of each fact (ADR 0656 §6); the catalog entry's `needs` already exists from G | +| §122 The cycle rule in the indexer's graph | the graph is the indexer's, and the checking program goes with the checker | +| §123 The builder's seat `mesh-builds` and its event `package-published` | the record has the builder say the publication as an event of its seat (ADR 0666 §15); its waits move only when a later change gives the seat its verbs | +| §124 Each state's object store `-objects` through mesh-sdk's small package, sent objects readable by a standing grant derived from the manifests, and the package's two tools | one object store per state, granted to its owner; the sender's declared callers and consumers read what it sends (ADR 0666 §16); only its users import it, so nothing else is rebuilt | +| §125 The `run` ask published by the runner's account and the indexer's; the verb `run` called by the sessions and the operator only | the steps and the validation run code from the change, in the sandbox only, and each asker reads only its own step keys (ADR 0363 §2); a run publishes tagged packages, so nothing else may ask one | +| §126 The checker's step `check` becomes `pipeline`; `kept` starts empty | the runner is an acting part in the checker's place (ADR 0666 §19); a step's result is taken only from its seat holder (ADR 0666 §21), so no verdict of the checker's is taken in, at the cost of one round of every module's tests | +| §127 The field `callers` in a seat definition replaces the holders' trusted `callers`, a missing field admitting the holder roles and the operator only | the record makes the definition the grants part's one source (ADR 0666 §20); a field left out must open nothing | +| §128 `widens` in `pipeline-ended`, computed by the running runner over every part serving a step, passed on by the teller | a widening is said and nothing refuses it (ADR 0666 §23, §26); a change must not compute its own | +| §129 `merge-pull` judged from the forge module's own `failed-steps`, heard from `step-ended`, any run at the head's fingerprint counting unless a later rerun of that step passed, the protection read again on every `branch-moved` | the record has it judge from the events it consumes, never by a call, so a head merges while the runner is down (ADR 0666 §11, ADR 0596) | +| §130 The new assigner before the new `merge-pull` and the runner, by `needs`, both switching on the first `registration-changed` | a merge with no Approve must never reach an assigner that writes wants without a pipeline (ADR 0667 §10) | +| §131 The forge bridge's finding `branch-unprotected` | a move of a branch whose protection was removed starts no delivering pipeline and is said (ADR 0666 §6) | +| §132 The verb `recovery` of `mesh-delivery` | a recovery is named by its author as a fact the bus keeps with who said it, never read from text (ADR 0666 §26, research 086 R10) | +| §133 No forge status for a pipeline | the step results live on the bus (research 086 R10), shown by the runner and the delivery record; a status would be a second copy outside it | +| §134 The runner's `pipelines` kept 90 days after a passed end on a watched branch, any other 7 days, its step log 100 days by age, its outputs 7, keyed by source revision | a retention the runner judges from its own facts, never from the gate's records (ADR 0596); the delivery record keeps the step events itself (§34), so nothing it traces needs the runner's key; one nothing delivered has no trace to serve after a week; about 300 pipelines a day must fit its caps, so they are 256 MiB and 128 MiB | +| §135 The forge module's `failed-steps` kept until no head it was seen at is a watched branch's head or an open pull request's | an entry judges only a head that can still be merged; once neither names it, nothing can be refused by it | +| §136 The gate's `source.` deleted when the branch's last watch goes | a branch no registration watches makes no revision, so its key would name nothing | +| §137 The sent store kept 7 days by age, with no person's word | an object sent carries one reply or event and is transport, not kept data (ADR 0595 §4); its readers fetch it within a call's bound or a consumer's lag | +| §138 A replaced object listed `key gone`, as a deleted key's is | no key names it, and one listing lets the drop tool take both on a person's word, as ADR 0595 §4 and ADR 0666 §16 have it | +| §139 The checker's removal after the first end-to-end pipeline, on one word, its buckets through `drop`, the snapshot kept 90 days | ADR 0667 §12 asks one word for all three; 90 days is the delivery records' retention, the longest a trace reads back | +| §140 The pipeline steps on the `check` stream after `delivery` | a delivery's build is never held by a pipeline, as a check never held one (ADR 0525 §4) | diff --git a/03-DESIGN/01-to-be/47-milestones/G-3-delivery-record-and-graphs.md b/03-DESIGN/01-to-be/47-milestones/G-3-delivery-record-and-graphs.md new file mode 100644 index 00000000..67f0b389 --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-3-delivery-record-and-graphs.md @@ -0,0 +1,376 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-10 +decisions: + - "ADR 0666" + - "ADR 0656" + - "ADR 0648" + - "ADR 0645" + - "ADR 0644" + - "ADR 0641" + - "ADR 0639" + - "ADR 0636" + - "ADR 0633" + - "ADR 0621" + - "ADR 0619" + - "ADR 0618" + - "ADR 0615" + - "ADR 0614" + - "ADR 0603" + - "ADR 0597" + - "ADR 0596" + - "ADR 0595" + - "ADR 0571" + - "ADR 0567" + - "ADR 0564" + - "ADR 0548" + - "ADR 0493" + - "ADR 0441" + - "ADR 0437" + - "ADR 0421" + - "ADR 0363" +--- + +# 47, milestone G: the delivery record, what the gate owes the forge, the state table, the two graphs and a change's graph difference + +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". + +## The delivery record, and what the gate owes the forge + +### The delivery record + +*ADR 0656; ADR 0595, ADR 0596, ADR 0597, ADR 0603 §7, ADR 0493 §3, §11, ADR 0633 §4, §5, ADR 0441 §1, ADR 0548 §8, +ADR 0564 §2, ADR 0363 §2.* + +§141 From research 085's milestone the gate, holder of the seat `mesh-delivery`, keeps one **delivery record** per +milestone's delivery, a ledger instance (ADR 0595's template `ledger`, ADR 0597) it alone writes (ADR 0656). It +holds: + +- §142 **the group's state**: the heads, the verdicts, the checks and the asks, which `merges` holds today; from milestone + G's delivery the pipelines' `step-started`, `step-ended` and `pipeline-ended` in place of the verdicts, the checks and + the asks, kept as long as the record (ADR 0666 §9); +- §143 **the trace**: the milestone, its issues, its pull requests, their commits, and the graph revisions the delivery + moved between; +- §144 **what happened to each entry on each node**: for every entry of the graph difference, taken in, built, checked, + composed, wanted, running healthy, held (with the hold's reason, so a reader of the record sees why an entry stands + still without asking its holder), failed (with what failed), or away (ADR 0656). + +§145 It holds no tiers, and only the gate orders work by it, so it is no second copy of the keys, the objection research +063 had to a stored plan (research 085, Findings 10). Its fields, retention and tools are this design's choices under +ADR 0656. + +- §146 **The outcomes arrive as events, through a durable consumer of the gate's own.** The gate's manifest `consumes` + each step event of *What each step says* (below), and `wait-held` and `wait-ended` of each holder. The consumer + resumes from the last event applied; an event applied twice changes nothing; an event is applied only when it comes + from its owner's account; and running healthy is taken from the node-engine seat's `running` event, never from a + have read across the seam (ADR 0596). +- §147 **It ends when every present node runs its entries**, and the gate says `record-ended` on the seat `mesh-delivery` + with the record's milestone and its nodes away. It names the nodes away, each with since when, and ends without + waiting for them, as a node away holds nothing (ADR 0603 §7). +- §148 **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. 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. +- §149 **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. +- §150 **An Approve given holds at its heads, until G's delivery.** An Approve the operator gave for a group's heads holds while those heads + stand: a check of the same heads asked again, after a main move or for any other reason, records its verdict in + the record and asks the operator nothing again (issue 1972). The Approve is withdrawn only as ADR 0441 §1 has it, + when a member stops being mergeable, and a carried verdict is read as current (ADR 0548 §8). From G's delivery no + Approve is asked per merge; an `approval` step's answer is the runner's, kept in the record as its step's end + (ADR 0666 §10). +- §151 **From G's delivery, a record for every landed merge.** A session merges through the seat `git` and the gate merges + nothing, so every merge is one the gate did not make: the gate opens a record for each source revision of a watched + branch the indexer takes in, a group's members in one, and `forge-made` no longer marks a record apart. A merge its + author names a recovery is marked `recovery` (*A recovery fix is an ordinary merge*). +- §152 **A small fix has a record of its own.** A small fix is a milestone of one issue (ADR 0493 §3), so it has a + delivery record like any milestone; nothing is merged outside a released milestone (ADR 0493 §11). +- §153 **It pins the graph revisions it points at.** The gate says, as the seat `mesh-delivery`'s events `pinned` and + `freed`, which catalog and assignment graph revisions a record starts and stops pointing at, and as + `heads-replaced` that a candidate's heads were replaced or its pull request closed. Only the gate's account may + publish them (ADR 0363 §2). The indexer and the assigner hear them through a durable consumer each, declared in + their manifests' `consumes`, which resumes from the last event applied after a restart, so a `freed` said while one + of them was down is still applied; neither reads the gate's state (ADR 0596; *Two graphs, one source of truth*, + below). +- §154 **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. +- §155 **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. +- §156 **The switch carries today's state in, once.** At the new gate's start, `merges` and every group in flight become + records, and from then on the gate writes `merges` no more; `turns`, which nothing writes while the check gates no + merge, is dropped with the hold (*Nothing merges into a repository under check*). 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 delivering milestone G again after a revert makes no second record and asks + nothing again. A revert of the gate is the delivery system's restore of its kept data (*A module's kept data on the + bus, backed up and restored with its delivery*). +- §157 **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`. +- §158 **What the gate owes the forge is said, not watched, and a merge made at the forge has a record too** (*What the + gate owes the forge*, below). +- §159 **Its tools**, against the four lines a module's tools answer: + + | Line | The gate | + |---|---| + | read everything it holds | `gate_record` (one record whole: the group's state, the trace, each entry's outcome per node, the nodes away), `gate_records` (every record with its fill against the value limit, and the ledger's fill against its cap), `gate_consumer` (its durable consumer: the last event applied per family, the events waiting, the age of the oldest) | + | do by hand what it does itself | `gate_record_repair` (for one record, asks each owner for the outcome of each entry missing one through that owner's read verb: the indexer's `take-in` read, the builder's build read, the checker seat's check read (from G's delivery the runner's `show`), the seat `mesh-wants`'s `entry`, and the node-engine seat's verb `composition` of that node; applies the answers as the consumer would) | + | recover without help | `gate_record_end` (end a record left open, a node gone for good or an entry that never runs, with the reason, recorded in the hand-act log of the seat `mesh-hand-acts`) | + | one seat, many tools | the seat `mesh-delivery`'s verbs, with `groups` answering a group's members and the acts said for it | + +- §160 **How it is checked.** In the gate's whole-module test: the gate stopped and restarted in the middle of a delivery + leaves the record complete with no outcome counted twice; the events `pinned`, `freed`, `heads-replaced` and + `record-ended` are said; a `freed` from an account other than the gate's is refused by the bus; a group approved + before the switch is not asked again after it, nor after milestone G is delivered again following a revert; a record ends once every present node runs its + entries, naming the nodes away; a held and a failed entry are kept with their reason; a record open past 24 hours + raises `record-open`, cleared by `record-ended`; a node away that wakes adds its outcomes and is no longer shown + away; a group's heads approved and checked again ask the operator nothing again; a small fix's merge gets a record + of its own; a record past its retention is removed and frees its revisions; the tools read a record, list the + records with their fill, and a repair completes a record missing an outcome through the owners' read verbs. In the + forge bridge's whole-module test, a group's members are read from `groups` and its end published again from it, and + a read of `merges` by its account is refused. Live: one call of `gate_record` traces an issue of the registry + milestone to each node's running build, also after six later merges; a node away is named with since when and its + outcome taken on waking; a record of 30 modules on four nodes stays under 256 KiB; a group checked again at old + heads after a main move is not asked of the operator again (issue 1972). + +### What the gate owes the forge + +*ADR 0596 §1, §2, ADR 0656 §7, ADR 0437 §8, ADR 0441 §5, ADR 0633 §1, §4, ADR 0363 §2, ADR 0564 §2; from research 085's +milestone, this design's choices under ADR 0596 and ADR 0656 §7.* + +§161 The forge bridge today performs what the gate's states `merges` and `owed` ask (a merge, a status, the delivery's +view, a comment, a line on the commit's note) by watching those keys (ADR 0437 §8), and the forge module writes the +checker's `mesh/check` from a watch of the checker's state `owed`: reads of another module's bus state that ADR 0596 +§2 ends when their owner moves. From research 085's milestone: + +- §162 **Every act is an event, under one id.** The gate says each act it owes as the event `owed` of the seat + `mesh-delivery`, on `mesh.seat.mesh-delivery.event.owed..`, carrying the act, the pull request, + the head and an id; the checker says its status the same way, as the event `owed` of the seat + `mesh-delivery-checker` on `mesh.seat.mesh-delivery-checker.event.owed..`. Only the owner's + account publishes each (ADR 0363 §2). The subject per repository and pull request lets the bridge, a person and a + test read one pull request's acts alone. The seat events are kept on each owner's stream `owed`, 7 days and at most + 64 MiB (an estimated 20 deliveries a day, with about 15 acts of about 1 KiB each, is about 2 MiB over 7 days). +- §163 **Every act is safe to repeat at the forge.** A status and the delivery's view replace what stands; a merge of a + pull request already merged at that head is answered performed; a comment and a line of the note carry the act's + id, and the seat `git`'s verb skips an id the pull request or note already holds. This is chosen over recording the + id before the act: an id recorded and then not performed (the bridge stopped between the two) would be lost, while + an act repeated changes nothing. The bridge's own state `performed`, a key-value state in the new form (ADR 0595), + form 1, one key per id with the outcome and when, kept 7 days as the stream is, its size the one its template fixes + (ADR 0595 §3), is therefore a + saving and a read, never what keeps an act from happening twice. +- §164 **Every outcome is said, and the gate records it.** The bridge says each act's outcome on its own stream `forge` as + the event `owed.ended`, with the id and one of `performed`, `refused` (the forge's own words), `could not be + performed` (the forge did not answer within the call's bound, with the error) or `undecodable` (an act it cannot + read, with why): an act it cannot decode is said, never only logged. The gate hears the stream `forge` through its + durable consumer, writes the outcome into the delivery record, and says an act's id is answered only once an + outcome is recorded. The checker does the same for its own acts: it hears `owed.ended` for its own ids, records each + outcome in its own state (it watches only its own state), says again at its start, under the same id, every act of + its own still unanswered, and lists one unanswered past 15 minutes in its own stalled read. +- §165 **What is unanswered is said again, and stalls by a bound.** At its start the gate says again, under the same id, + every act its records hold unanswered. An act unanswered 15 minutes after it was said is listed by `stalled`, which + the condition keeper raises as the stalled line (ADR 0421 §7, ADR 0564 §2): fifteen minutes is many times a + forge call's time, and short beside the check's hour. +- §166 **A group's merges follow one another, until G's delivery.** The gate says a group's next `merge-pull` only once the previous member's + merged message is heard (ADR 0441 §5), so a member the forge refuses holds the rest, and none is merged ahead. +- §167 **From G's delivery the gate owes no merge, and the checker nothing.** A session calls `merge-pull` itself (ADR 0666 + §11), and a group's members land one by one, the group ending when every member has (*Groups*); the checker's `owed` + goes with the checker, and the runner owes the forge nothing, a pipeline's result living on the bus. The gate still + owes the delivery's view, a comment and the note's line, performed by the forge bridge as above. +- §168 **The bridge catches up from the seat.** At its start the bridge resumes its durable consumer and also reads + `groups`, which answers, per group, the acts the gate said with their ids and their recorded outcomes; an id said + and not answered is performed. Its tools: `bridge_owed` (the ids it performed with their outcomes, and its + consumer's backlog: the last event applied, the events waiting, the age of the oldest), `bridge_owed_again` + (perform one id again now) and `bridge_owed_drop` (drop one id with its reason, written in the hand-act log of the + seat `mesh-hand-acts`, and said as `owed.ended` with the outcome `dropped`). +- §169 **The switch carries what is owed.** At the new gate's start (*The switch carries today's state in, once*) every key + of `owed` and `merges` the old bridge has not performed becomes an act of its record, with an id made of the key and + its revision; the new bridge counts as performed every id whose key and revision the old bridge's own bookkeeping + names, so nothing is done twice and nothing dropped. A merge handed to the forge during the switch is such an act: + its merged message finds the record holding it, so it opens no `forge-made` record. +- §170 **A merge made at the forge has a record too.** A merge the forge reports for which no record holds a `merge-pull` + of that pull request at that head (a person merged it in the forge's own page, as on 2026-11-07 and 11-09) is taken + in like any other merge (*Taken in by the indexer it names*), and the gate opens a delivery record for it, marked + `forge-made`, with no Approve, so its trace and its outcomes on the nodes are kept like every delivery's and no + later merge waits behind a merge the gate never heard. The gate says it as the delivery's `transition` with the why + `forge-made`, which the teller passes on to the operator (the operator, 1921's note of 2026-11-07: "a merge made in + the forge is taken in like any other"). +- §171 **How it is checked.** In the forge bridge's whole-module test: an `owed` said while the bridge is stopped is + performed once after its restart; the bridge stopped between an act and its `owed.ended` performs it again on its + restart and the forge holds it once (the comment and the note by their id); an act said twice is performed once; an + undecodable act is said `undecodable`; `bridge_owed`, `bridge_owed_again` and `bridge_owed_drop` answer, the drop + writing the hand-act log; its account cannot read the gate's or the checker's state. In the gate's: an act left + unanswered is said again at the gate's start under its id and listed by `stalled` after 15 minutes; a group's second + `merge-pull` is said only after the first member's merged message; the switch with a merge handed and not yet + merged makes no `forge-made` record; a merged message with no record's merge opens a `forge-made` record, said as + `transition` with why `forge-made`, and a later merge in the same repository is taken in behind it. Acceptance + tests 53 and 51. + +## The state table, from milestone G's delivery + +§172 **From milestone G's delivery the gate keeps no merge states.** With no check before a merge and no Approve per merge +(ADR 0666 §10, §17), nothing is `proposed`, `ready`, `rejected` or `held` before a merge: a commit's states are its +pipeline's steps, kept by the runner (*Pipeline steps and the policy*). The table, the gate's `table`, `recheck` and +`close` and the one key per pull request go with the check; `stalled` lists the acts owed to the forge past their 15 +minutes and the records `record-open` names. + +## 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 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.* + +§173 From research 085's milestone. One graph answered two questions, which modules exist and which version runs where, +and one module running as several placed versions would have given one entry two shapes and one key two writers; two +graphs answer them, the second computed from the first, so there is one source of truth (ADR 0656; research 085, +Findings 11). How each is kept, read and retained is this design's choice under ADR 0656. + +``` + manifests ──► indexer (mesh-catalog) ──► catalog graph revision ──┐ + ├──► assigner (mesh-wants) ──► assignment graph revision + placements (mesh-module-assigner) ───────────────────────────────┤ │ │ + node capabilities (node-engine seat) ────────────────────────────┘ │ └─► wants, from a trunk revision only + ▼ + a change's assignment graph − the current one = its graph difference = its delivery +``` + +- §174 **The catalog graph is the indexer's.** The indexer, holder of the seat `mesh-catalog`, keeps the modules as their + manifests declare them; it is today's module graph, and *module graph* stays its other name (ADR 0656). At each + revision it holds one entry per module: its bases, its packages, the toolchain version and recipe version of the + build module that builds it as ADR 0603 §2 has them, its provides and requires, its invokes and serves, and its + fingerprint, with the rules the seats declare (*An acting part's new version acts first*, below). +- §175 **The assignment graph is the assigner's, computed and never written by hand.** The assigner, holder of the seats + `mesh-module-assigner` and `mesh-wants`, computes the module versions placed on nodes, with the edges between + versions, from the catalog graph, the placements and the nodes' capabilities, and nothing else writes it + (ADR 0656). Each entry is one module version on a node; its edges are provisioning, contribution, and the acting + parts' versions that are to act on it (ADR 0656), whose rules and versions come in with the catalog graph. It is + kept, but it is no stored plan: it has one writer, no part orders work by a stored plan, and any revision is computed + again from the inputs it names and compared with the one kept, by a tool (below), so a kept revision is a checked + derivation, never a second source of the kind research 063 removed (research 085, Findings 5). +- §176 **Each graph is kept per revision, and each revision names its inputs** (ADR 0656). The indexer keeps a catalog + graph revision for each source revision it takes in; the assigner keeps an assignment graph revision for each change + of its inputs, naming the catalog graph revision, the placements and the capability readings it was computed from. + Both parts are the foundation (ADR 0619 §1), so both keep their revisions as objects declared in an `objects` + section in the old form, each with a cap it declares there, never in the new form (ADR 0619 §2), and neither waits + on the bus module to start; the manifest validator refuses either manifest that declares them in the new form + (ADR 0619 §3). An assignment graph revision is announced by an event of the seat `mesh-wants` and served by its + verbs. This replaces ADR 0603 §2's one key per module with five past values as the graph's form (ADR 0656, which + changes ADR 0603 §2); the keys are written no more, and a revert of milestone G restores them from their backup + (*Reverted*, below). +- §177 **A revision is made by a merge, and also with no merge.** The indexer makes a catalog graph revision when it + takes a source revision in. The assigner makes an assignment graph revision on each new catalog graph revision, on + the seat `mesh-module-assigner`'s event `assignments-changed` (ADR 0567 §5, ADR 0641), and on the node-engine seat's event + `capabilities-changed`, which a node-engine says when the capabilities it states in its node record change + (ADR 0614 §4), so the assigner follows an event and never reads a node record across the seam (ADR 0596). A + placement change is traced like a merge. At its first start with no revision kept, the indexer takes in the head the + gate's source names for every repository by its catch-up path and keeps that as revision 1, and its verb `revision` + answers that it waits until then (lead-47's ruling in G's prepare: a first start has no merge to make a revision). +- §178 **A revision is kept while a record pins it or it is current.** A revision of either graph, and the input revisions + it names, is removed when no delivery record pins it and it is not current: neither trunk's newest revision nor the + newest of an open candidate. Pins are counted per revision, so a revision two records point at stays until both + 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. +- §179 **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. +- §180 **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. +- §181 **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. 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. +- §182 **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). +- §183 **The want stays the one record a node reads.** The assigner writes `want..` from a trunk + revision's assignment graph only; a node-engine reads its want, never the assignment graph, and the have stays what + it runs (ADR 0656, which changes ADR 0571 §8). +- §184 **From G's delivery, per watched branch and between artifacts.** The catalog graph is kept once per watched branch, + each revision naming its branch, and its edges run between artifact references, of the six kinds of *Dependencies + between artifacts* (ADR 0666 §6, §14). The assigner computes a node's entries from the catalog graph of the branch its + placement names, and writes a want from a revision of that branch whose `required` steps passed, in place of a trunk + revision's alone (ADR 0666 §9, which changes ADR 0656 §2). +- §185 **Reverted.** Only the new form is written: the indexer, the assigner and the gate keep nothing of ADR 0603 §2's + keys or of `merges` and `turns` beside the new forms, since nothing reads them after milestone G. A revert of + milestone G is a change like any other, checked and delivered, and the delivery system restores what it must + (*A module's kept data on the bus, backed up and restored with its delivery*, under *A backup before the apply*). + Milestone G changes the form of these kept data, and raises their `form` (a kept data declaring none reads as form + 0, so G's own move takes their restore points): + + | 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`; the states `holds`, `engine-reads` and `hold-bounds` of its mesh-wide hold | catalog graph revisions as objects, form 1; `modules` no longer declared, nor its tool `indexer_read_modules` or its view; the mesh-wide hold, its three states and its tools `indexer_holds` and `indexer_engine_reads` gone, the wait per node being the assigner's (*Composing waits per node, by the assigner*); every reader of it reading the catalog half (*Every other reader of which modules exist*, under *The revision view*, below) | yes: `modules` and the hold's three states as they 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 | + + The wants keep their form: their keys and values are unchanged, only what the assigner computes them from changes, + so G raises no `form` on them and a revert restores none. Restoring the wants would send nodes back to older + 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. 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 + +- §186 **The difference names everything a plan named.** For a change (a pull request's heads, a delivery group's + heads, or a merge), the indexer gives its catalog graph and the assigner its assignment graph. Compared with the + current revision, the difference names the new versions, the moved edges, the touched nodes and the order, read off + the edges, and no plan is stored (ADR 0656). The assigner serves it by the seat `mesh-wants`'s verb `difference`, + for two revisions of the assignment graph or for a candidate against trunk. ADR 0603 §6's tiers, as ADR 0633 §5 + changes it, are a view worked out from that difference (ADR 0656, which changes ADR 0603 §6), read through + `difference`, with the waits and holds read through their owners' tools and the pending revisions through + `indexer_pending`. +- §187 **The preview is a candidate's difference.** Issue 1874's plan preview of a group before its merge is its + candidate's difference, read by one call of `difference`, and needs no code of its own: a candidate's graphs are + computed for its check anyway, so the preview costs nothing more. +- §188 **A changed fingerprint with no difference is refused by name.** When a module's fingerprint in the change + differs from the running one while the difference is empty, the assigner refuses it, naming the module, and the + check fails naming it. +- §189 **A candidate's graphs are kept apart, and dropped with its heads.** A candidate's two graphs are computed per + check, kept apart from trunk's, and dropped on the gate's event `heads-replaced`, said when the candidate's heads + were replaced or its pull request closed. A candidate costs a computed graph per check. +- §190 **Only a trunk revision writes wants**, whether a merge taken in or a placement or capability change made it; a + candidate's graph writes none (ADR 0656). From G's delivery a want comes from a revision of the watched branch a + placement chose whose `required` steps passed, and a candidate is a run on request's: the indexer builds it on the + runner's `run-started` and drops it on that run's `pipeline-ended`, the gate's `heads-replaced` no longer making + candidates (ADR 0666 §9, §18). +- §191 **Each part acts on its own entries.** The builder, the build modules and the grants part each act when their own + entries in the new revision differ from what they run (ADR 0656), reading them through the seat `mesh-wants`'s verbs + and events, never its bus state (ADR 0596), and naming no other module (ADR 0656). The node-engine acts on its want + alone, as it does today: the want is the one record a node reads, and handing every node the assignment half would + have each node read every other node's placements. +- §192 **A node asleep catches up when it wakes.** A node away holds nothing and catches up by its wants (ADR 0603 §7); + a want's new form reaches only a node-engine that says it reads it (ADR 0614 §4, ADR 0618 §2). +- §193 **No delivery goes ahead of a change.** A change is delivered as its own difference, its acting parts' new versions + first in their own steps (ADR 0656); the one exception is the delivery before research 085's milestone (*The + bootstrap delivery, and the one milestone after it*, under *Milestones*; ADR 0644). diff --git a/03-DESIGN/01-to-be/47-milestones/G-4-revision-view-and-steps.md b/03-DESIGN/01-to-be/47-milestones/G-4-revision-view-and-steps.md new file mode 100644 index 00000000..b4cfe0a4 --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-4-revision-view-and-steps.md @@ -0,0 +1,354 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-10 +decisions: + - "ADR 0667" + - "ADR 0666" + - "ADR 0663" + - "ADR 0660" + - "ADR 0656" + - "ADR 0644" + - "ADR 0641" + - "ADR 0639" + - "ADR 0636" + - "ADR 0635" + - "ADR 0625" + - "ADR 0621" + - "ADR 0619" + - "ADR 0615" + - "ADR 0614" + - "ADR 0603" + - "ADR 0596" + - "ADR 0584" + - "ADR 0564" + - "ADR 0548" + - "ADR 0525" + - "ADR 0498" + - "ADR 0493" + - "ADR 0448" + - "ADR 0441" + - "ADR 0404" + - "ADR 0363" +--- + +# 47, milestone G: the revision view, what ADR 0656 leaves to this design, the choices under ADR 0656, what each step says and their tools + +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". + +### The revision view + +- §194 **A pair, each half served by its one owner** (ADR 0656). For a trunk revision, or for a candidate's heads before + any merge, the indexer serves the catalog half by the seat `mesh-catalog`'s verb `revision`: the catalog graph, the + heads, the seat definitions, the manifests' verdicts and the fingerprints. The assigner serves the assignment half by + the seat `mesh-wants`'s verb `revision`: an assignment graph revision, named by its own revision and naming its + inputs, the catalog graph revision, the placements and the capability readings. A part's view for the current + revision is the newest assignment half and the catalog half it names. `seat-definitions` stays the seat list's verb + (ADR 0636 §4), and the catalog half carries the same definitions at its revision. +- §195 **Every part takes a first view, then follows the events.** The checker, the builder, the grants part and the + indexer's own later passes take a first view from the two verbs and then follow the two seats' events, never a list + compiled into their build nor another module's bus state (ADR 0656, ADR 0596). The node-engine reads only the catalog + half, for the seat definitions it composes with, and its want; it never reads the assignment half (*Each part acts on + its own entries*, above). Of the view the assigner reads only the catalog half, never its own output back; its other + inputs stay the placements and the capabilities (ADR 0656). A running part acts only on a trunk revision's view; a + candidate's view is read only by the parts that judge a candidate, inside the check its manifest validator and + composer, and the indexer's candidate pass (ADR 0656, which changes ADR 0636 §4 in that the candidate pass joins + them). From G's delivery a running part acts on a revision of a watched branch as on a trunk revision, and a + candidate's view is read only by a run on request's steps and the indexer's candidate pass (ADR 0666 §6, §18). +- §196 **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*, + 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). 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 has no view + today; milestone G adds there a view follower with its read and ask-again tools, beside the proved caller, a + verb's `callers` and a seat's publish, 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. Its cost: a release of the TypeScript side rebuilds every TypeScript module, which + the delivery carries like any change of a package its users build in. + - **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 0663, which changes ADR 0621 §1, §2 and + ADR 0615 §3; accepted); 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). +- §197 **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*). +- §198 **The indexer reads every definition before it judges.** At its start the indexer reads the seat definitions of + every contracts module's trunk through the forge (the seat `git`'s verbs) before it judges a manifest or answers + `seat-definitions`, and only then follows the forge bridge's stream; a definition merged while it was down is + therefore never judged against a list that lacks it. The design's choice under ADR 0636 §1 and §4, as the operator + asked it (1921's note of 2026-11-07: "the indexer reads its seat definitions at start"). How it is checked: in the + indexer's whole-module test, started with a definition on a contracts trunk that no event announced, the first + manifest claiming that seat is judged with it. +- §199 **Who holds a seat is applied by one rule, from what each node runs.** The node-engine and the grants part decide a + seat's pass by mesh-sdk's `SeatsToPassOn` (ADR 0660 §3), reading the manifest of the build each assignment runs + (ADR 0660 §2). What each node runs is heard, never asked: each part keeps it from the node-engine seat's events + `running..` (*What each step says*), through a durable consumer of its own declared in its `consumes`, + so a pass is decided with no call to a node that may be away. The seat's stream keeps the last message per subject + with no age limit, so the last `running` of every module on every node stays on it however long ago it was said, + and each part's durable consumer starts from the last message per subject, never from now: a consumer made after a + node's last `running`, and two parts' consumers made at different times, hold the same builds. The event carries the module, the fingerprint and the + digest of the build's manifest, which is the indexer's manifest of that build (ADR 0660 §2's "the indexer's + manifest of it"), read by digest from the catalog half; the builder's done key is not needed, since the event names + the digest it would give. A node that has said no `running` for a module since the part's consumer began counts by + the trunk's manifest, as ADR 0660 §2 has it where the running build cannot be read, and both parts apply that same + fallback in the one function (ADR 0660 §3). Never another node's have key, its want or the assignment half (ADR 0596 + §2; *Each part acts on its own entries*). The design's choice under ADR 0660 §2: the node that runs a build says what + it runs, and an event kept by a durable consumer answers when that node is away. How it is checked: test 56. +- §200 **A part with no view says it waits.** It names the revision it has no view for, acts on nothing new, and keeps + running what it runs, as ADR 0636 §1 has a reader of the seat list do; the checker, the builder and the grants part + wait too, and wait for the view, not only the seat list (ADR 0656, which changes ADR 0636 §1); the node-engine waits + for the catalog half as ADR 0636 §1 already has it wait for the seat list. Each answers in its runtime's first-view + tool (below) the revision it holds, the one it waits for and since when, and after five minutes without a view writes the finding + `view-waits` naming itself and the revision, as a reader of the seat list does, which the condition keeper raises + (ADR 0564 §2); it clears when the view arrives. Five minutes is the bound the seat list's readers already keep, so an + indexer or assigner down for a restart raises nothing. A part restarted while the indexer or the assigner is down + waits and says so. +- §201 **Each process knows its own build from its start.** The node-engine hands every process it starts the + fingerprint of the build it starts it with, and the process takes its own build from that, never learning it later + (ADR 0656, which changes ADR 0639 §7 from the indexer to every process). *The fingerprint handover*, below, says its + form. + +### What ADR 0656 leaves to this design + +§202 ADR 0656 leaves how each part is built to this design, under issue 1921; these four needed a shape of their own. + +- §203 **An acting rule's fields.** The seat's definition is where ADR 0656 has an acting part declare what it acts on. A + seat's definition gains one optional field `acts`, a list of rules, each with three fields of its own: `step`, one of `take-in`, + `validate`, `check`, `build`, `compose`, `grant` and `place`, and from G's delivery `pipeline`, the runner's, in + `check`'s place (ADR 0666 §19); `on`, either `every` (every module) or a `language` + naming the language of the modules it acts on, matched against what the manifest says its language is, as the + indexer's language map matches today; and `bound`, how long an entry waits in that step for the acting part's new + version (*An acting part's new version acts first*, below, gives each step's default). The definition format's own + schema (design 26) gains the field, and the format stays 1. The indexer reads it into the catalog graph, the assigner + into edges, and the checking program from the revision view. **Its cost in mesh-sdk:** the seat-definition reader in + mesh-sdk's runtime refuses a definition with a field it does not know or a format other than 1, so a reader built + before the field refuses every definition that carries `acts`, and a refused definition blocks new claims of that + seat only (ADR 0636 §3), never what runs. The runtime therefore learns `acts`, which rebuilds every module once + (ADR 0656, Consequences); that is the same rebuild the fingerprint handover makes, so the milestone rebuilds every + module once in all, and no other module's code changes. A definition carrying `acts` reaches only readers built with + that runtime: the seat definitions that gain it are members of milestone G's group, and every reader is rebuilt in + the same delivery; the format's reader itself is delivered before them, as a release of mesh-sdk's seat-definition + reader merged ahead of G's check (the operator's choice of a reader first, 2026-11-09; ADR 0667 §4). The bootstrapped + definitions every reader carries from its start (ADR 0636 §2), of which `node-backup` carries no `acts`, are made + again from the new definitions in the same change, and mesh-sdk's existing test that each bootstrapped copy equals its definition holds + them to it. +- §204 **One wiring of the seat list's reader.** The reader of the seat list and its two tools of ADR 0636 §7 are wired once, + by mesh-sdk's runtime (`go/seatlist`), for every part that reads the list; no part wires its own. Four delivery + parts (the assigner, the checker, the indexer and the grants part) each carry a hand-wired copy today, each with its + own pair of tools, and each copy would have to learn `acts` apart. The design's choice under ADR 0636 §7, which puts + the tools in the runtime bundle: one wiring learns `acts` once, and every part's two tools are the same, named by + their module, with the same callers (any person and the healers to read, the operator and the healers to read + again), the node-engine's among them. How it is checked: mesh-sdk's seat-list test registers both tools for a + fixture part; a search of the delivery parts' code finds no wiring of its own. +- §205 **The fingerprint handover.** The node-engine starts each process, a service or a container alike, with the + environment value `MESH_FINGERPRINT`, the fingerprint of the want it applies; mesh-sdk's runtime reads it once at the + process's start and holds it as the process's own build (ADR 0656). A process started with no value says `own build + unknown` in its runtime tool and its journal, and the indexer acts on it as ADR 0639 §8 has it. **Its cost:** one + change of mesh-sdk's runtime, which rebuilds every module once (ADR 0656, Consequences), and one of the + node-engine; the order between them is the composing step's wait of the node-engine (*Order without a person*, + below), so a module whose runtime reads the value is composed only by a node-engine that hands it. +- §206 **The build seat's ask that runs a program in the sandbox.** The checker asks the seat `node-build-go` for an ask + `run` on the build seats' `check` stream: one whole job on one node (ADR 0525 §3), which fetches the named heads, + builds the named program at them, runs it in the run step (ADR 0448 §2) with the input the ask names by its sha256 in + the asker's own state (the checker's `inputs`, a day), asked of the seat `mesh-delivery-checker`'s verb `input` and + checked against that digest, laid as a file, and writes the program's exit and its answer, carried whole, 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 from G's delivery the runner's and the indexer's (its validation before take-in), each under its own name. + Every step key of a `run` ask starts with the asker's name (`checker.`), 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. From milestone G the seat definition of `node-build-go` in `mesh-delivery-contracts` names it; B0 + brings the ask with its types in mesh-sdk's `go/delivery` and the grants check asks have today (*The bootstrap + delivery, and the one milestone after 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. 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. +- §207 **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 + for a build node` when no build node of the language took the ask within the bound, and `did not build` when one + took it and the build failed, so the operator knows whether to free a build node or fix the change. + +### Choices this design makes under ADR 0656 + +§208 ADR 0656 decides the direction and leaves every choice made in working it out to this design, each with its reason +(ADR 0656, Consequences; the operator, 2026-11-06 20:3x). The choices, and why: + +| Choice | Why | +|---|---| +| §209 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 | +| §210 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 | +| §211 No old form written beside the new; a revert restores the parts' kept data on the bus from a restore point taken before they moved, then they catch up forward | only the new form is looked after (the operator, 2026-11-06 21:3x), and a revert still works, as the delivery system's capability for every change of a kept data's form | +| §212 A module's kept data on the bus backed up before a `form` change and restored beside the live data at a revert, a missing restore point holding the revert | ADR 0404 covers only the items on a module's node, so the bus's kept data had no restore point; a restore never goes over live data, and a revert with nothing to restore must not start on the newer form unasked | +| §213 Only kept data whose `form` changes restored, the wants never; at a revert the node-engine last on its node, and the reverted assigner and gate only after the reverted indexer took in the trunk's head | restoring anything else would set back what was decided since; restored wants, or an assigner reading an indexer not yet caught up, would send nodes to older fingerprints; the node-engine of before may not know how to restore | +| §214 A restore point holding the consumers' positions and the catch-up tool the newer manifest declares, called after a restore | the reverted build then hears everything since as after any restart; the stream `forge` keeps 7 days, so what fell out of it is taken in from the trunk by the part's own tool | +| §215 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 | +| §216 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 | +| §217 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 | +| §218 `record-open` only when no other condition covers the record | the cause is already told once; a second condition for it is noise | +| §219 `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 | +| §220 The switch into records keyed by milestone and heads, safe to run twice | delivering milestone G again after a revert makes no second record and asks no group again | +| §221 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 | +| §222 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 | +| §223 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 | +| §224 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 | +| §225 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 | +| §226 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) | +| §227 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) | +| §228 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 | +| §229 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 | +| §230 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 | +| §231 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) | +| §232 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 | +| §233 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 | +| §234 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 | +| §235 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 | +| §236 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 | +| §237 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 | +| §238 The acting rule's field `acts` with `step`, `on` and `bound`, the definition format staying 1 | the steps and the language match are what the indexer's map and the checker's cases held in code (research 085, Findings 2); the reader refuses an unknown field, so the runtime learns it in the one rebuild the milestone makes | +| §239 The fingerprint handed as `MESH_FINGERPRINT` at a process's start | one value every runtime reads before anything else, a service and a container alike | +| §240 The delivery record's consumer, held and failed outcomes, end and `record-ended`, the finding `record-open`, node away and wake, switch done once, and tools | a restart must lose and double nothing; a reader sees why an entry stands still; a node asleep must not hold a delivery open; a switch run twice must ask no approved group again | +| §241 A small fix's own record | a small fix is a milestone of one issue (ADR 0493 §3) | +| §242 An Approve given held while its heads stand (issue 1972) | the operator approved those heads; a check of them asked again changes nothing they approved (ADR 0441 §1, ADR 0548 §8) | +| §243 The milestone events on a subject of their own | a waiter on one member's pull request otherwise wakes on every member's | +| §244 The issue tracker's link kinds to a decision and a research effort, and the knowledge base's link from a decision to its milestone | the trace from research to the running build is then links all the way, with no text match (research 085, Findings 7; ADR 0493 §4) | +| §245 The view's state read and asked again by mesh-sdk's `go/views` tools `_first_views` and `_first_view_again`, which gain the revision a part holds and what it waits for; the typed first view and the following of each half in the seats' client packages `meshcatalogclient` and `meshwantsclient`, built on `go/views`; the finding `view-waits` after five minutes; and `node_process_builds` | every part's view and every process's own build is read and steered by a tool; `go/views` already follows an owner's events durably and serves those two tools, so a second follower or a second pair of tools would be two forms of one thing; a view tool typed by the two seats would make mesh-sdk import contracts (ADR 0635 §4), so the typed half sits in the seats' client packages (ADR 0625 §4); a part waiting for the view is raised on the bound the seat list's readers keep | +| §246 The seat list's reader and its two tools wired once by mesh-sdk's runtime | ADR 0636 §7 puts them in the runtime bundle; four hand-wired copies would each learn `acts` apart | +| §247 The trunk's heads from the catalog half, and nothing read of the gate by the checker | ADR 0596 §2; with the hold and turns removed nothing of the gate is left for the checker to read | +| §248 The indexer reads every contracts trunk's definitions at its start, before it judges | a definition merged while it was down is never judged against a list that lacks it (ADR 0636 §1) | +| §249 Another node's running build kept from the node-engine seat's `running` events by a durable consumer, the event carrying the fingerprint and the manifest's digest, the trunk's manifest for a node that said nothing | ADR 0660 §2 reads the manifest of the build each node runs; an event kept by a durable consumer answers while a node is away, where a call would wait on it; one fallback in the one function keeps the two parts agreed (ADR 0660 §3) | +| §250 What the gate and the checker owe the forge said as the event `owed` per repository and pull request, each act safe to repeat at the forge, each outcome said as `owed.ended` and recorded, an unanswered act said again at the gate's start and stalled after 15 minutes | ADR 0596 §1, §2 end the watches of the gate's and the checker's keys; an act safe to repeat cannot be lost between the act and its record, where an id recorded first could; an outcome said and recorded leaves no act unseen | +| §251 A merge made at the forge opens a `forge-made` delivery record | its trace and outcomes are kept like every delivery's, and no later merge waits behind a merge the gate never heard | +| §252 Every delivery part and the gate reach the forge only through the seat `git`'s verbs and events, the act verbs granted by the holder's trusted `callers` to the forge bridge alone, a failed read answered as an error | the delivery parts name no other module (ADR 0656 §1, acceptance test 27); the forge's answers are typed in the seat's contract instead of guessed in each caller; the bridge stays the one part that acts on the forge; an error read as an empty answer would hide a forge down | +| §253 A check that failed because a part of the mesh was not heard says `could not run`, naming the part, releases its claim, and is asked again once per heads and part on that part's `running` event, raising `check-could-not-run` after 60 minutes | the operator sees a mesh fault apart from a failing change, and the check is asked again on the one event that can change its outcome, never on a timer (issue 1373) | +| §254 A module held for its claim raises `module-held` until it is composed | a module left out in silence would look delivered (the mesh's rule that failure is loud) | +| §255 The indexer waits for the seat `git` at most 2 minutes at its start, then judges from its kept definitions and raises `view-waits` after five | longer than a forge's restart, and no start held behind a forge down for good | +| §256 The forge before the forge bridge before the gate, reverted the other way | the verbs are served before they are called, and `owed` is said only to a bridge that hears it | +| §257 The store seat's holder's own new build kept on its build node and fetched by `kept-artifact` when its seat is broken | its own build needs the store it replaces; a step of the delivery replaces the hand bootstrap | +| §258 Every drop and release tool writes the hand-act log | a hand act that changes what the mesh runs or keeps is recorded with who did it and why, as every hand act is (seat `mesh-hand-acts`) | + +§259 Each choice is checked by the tests its section names; the review by architectural domain judges them. + +### What each step says + +§260 Each step says what it did as its own event, the event of the part that did it. A catalog graph revision kept +carries the source revision; an assignment graph revision kept and a difference worked out carry the source revision +and the assignment graph revisions; an entry taken in, built, checked, composed, wanted and running also carries the +entry's fingerprint and its node. The gate's delivery record is built from them. The seat's event comes first where +the parts follow it through a seat (ADR 0656); a holder's own event stays under its own subject, declared in its own +manifest's `streams` and published on `mesh.stream..event.` (ADR 0363 §1, §2). A seat's event is +declared in its definition and its holder's manifest claims the seat; a seat event's name has no dot, since it is read +as a subject's token, and it is kept on the stream `SEAT__`, which the grants part makes with its durable +consumers, keeping it by the age and cap the definition's `keep` names, 90 days unless it names less. **An event a node publishes carries the node in +its subject**, and the grants part grants each node-engine's account only its own node's subjects, so one node cannot +say another runs a build (ADR 0363 §2); the gate's events are granted to the gate's account alone. + +| Step | Event | Owner: the seat, then the module | Subject | Declared in | +|---|---|---|---|---| +| a catalog graph revision kept, saying whether it is a trunk revision or a candidate's and its watched branch | `catalog-revision` | `mesh-catalog`, the indexer | `mesh.seat.mesh-catalog.event.catalog-revision`, the watched branch in the subject from G's delivery | the seat's definition in `mesh-delivery-contracts` | +| an assignment graph revision kept | `assignment-revision` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.assignment-revision` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | +| a difference worked out | `difference` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.difference` | the same | +| an entry taken in | `take-in.taken`, which gains the entries | the indexer's own | `mesh.stream.mesh-delivery-indexer.event.take-in.taken` | the indexer's manifest | +| an entry built | `build.ended` | the builder's own | `mesh.stream.mesh-delivery-builder.event.build.ended` | the builder's manifest | +| an entry checked | `checked` | `mesh-delivery-checker`, the checker | `mesh.seat.mesh-delivery-checker.event.checked` | the seat's definition in `mesh-delivery-contracts` | +| an entry wanted | `wanted` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.wanted` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | +| an entry composed, and running healthy | `composed`, `running` | `node-engine`, the node-engine on each node | `mesh.seat.node-engine.event.composed.`, `.running..`, the latter on a stream kept last per subject with no age limit | 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.` | the same | +| 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..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..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.` | the seat's definition in `mesh-delivery-contracts` | +| a milestone's ask asked, withdrawn and answered, until G's delivery | `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.`, the milestone in the event, on the seat's family stream, apart from each delivery's `transition` | the same | +| a delivery record opened, and a merge named a recovery | `record-opened`, `recovery-merged` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.` | the same | +| a pipeline, its steps, its waits and a run on request, from G's delivery | `pipeline-started`, `pipeline-ended`, `group-ended`, `run-started`, `step-started`, `step-ended`, `wait-held`, `wait-ended` | `mesh-pipeline-runner`, the runner | `mesh.seat.mesh-pipeline-runner.event....` | the seat's definition in `mesh-delivery-contracts` | +| a pipeline step ended, from G's delivery | `step-ended` | each build seat, `mesh-wants` | `mesh.seat..event.step-ended.`, the node that ran it | each seat's definition | +| a registration changed; a package published, from G's delivery | `registration-changed`; `package-published` | `mesh-catalog`, the indexer; `mesh-builds`, the builder | `mesh.seat.mesh-catalog.event.registration-changed`; `mesh.seat.mesh-builds.event.package-published` | the seats' definitions in `mesh-delivery-contracts` | + +§261 The events, their fields, their subjects and which are a seat's are this design's choices: a seat's event where +other parts follow the step through the seat, a holder's own event where only the delivery record hears it, so that +nothing reads another module's bus state (ADR 0596). The builder's waits go on its own stream because the builder +holds no seat of its own; the watchman and the gate follow them there. How it is checked: each seat definition's +conformance fixtures name the subject of every event it gains, and a case of the holder's whole-module test matches an +event it publishes against them, as for every seat (design 26); the gate's whole-module test hears each row through +its durable consumer, with exactly the fields this section gives it; a `running` event published by one node's account +on another node's subject is refused by the bus. + +### Their tools + +| 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), `assigner_puts` (each put with its step, since when, held or not, the `running` that ended it, and for a put without hold whether it is past its 30-minute bound; a put older than the trunk's newest build of its module said so) | `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` | + +§262 Every drop and release tool writes its act, who did it and why, in the hand-act log of the seat `mesh-hand-acts`. +Beside ADR 0636 §7's two tools in mesh-sdk's runtime bundle, which read a reader's seat list and make it read the +list again, the view is read and steered by the two tools mesh-sdk's `go/views` already serves every part that takes a +first view: `_first_views` gains which view revision the part holds, what it waits for and since when, beside +what it heard and how far it is behind, and `_first_view_again`, granted as ADR 0636 §7's second is, makes it +read the view again now. `go/views` stays seat-neutral, naming no seat and no seat's type (ADR 0635 §4); the typed +first view of each half and the following of its events are the seats' client packages, `meshcatalogclient` for +`mesh-catalog` and `meshwantsclient` for `mesh-wants`, each built on `go/views` (ADR 0625 §4), so no part follows the +view by a follower of its own. A tool +of the node-engine, `node_process_builds`, reads the build each process was started with. The tools answer the four +lines every module's tools answer (design 26), so that every state these parts keep is read and every act they do is +steered by a tool. + +§263 **How it is checked.** Unit (assigner): a revision of the assignment graph computed again from its named inputs +equals the one kept; a fixture change with a new fingerprint and an empty difference is refused, naming its module; +one revision of about 120 modules on four nodes stays under the bus's value limit; of the view the assigner reads only +the catalog half. Unit (indexer): a catalog graph revision holds one entry per module of its source revision. Whole +(indexer): a source revision taken in gives one catalog graph revision kept as objects in an `objects` section in the +old form within the cap its manifest declares; the manifest validator refuses the indexer's or the assigner's manifest +that declares them in the new form. Whole (indexer, assigner): a revision a delivery record pins is not removed; one +two records pin stays until both free it; one neither pinned nor current is removed; a `freed` said while the part +was stopped is applied after its restart; a candidate's graphs are dropped on `heads-replaced`; a store past 80 % says +`filling` and raises `store-filling`, and a full one says `full` and refuses its next revision naming itself; for a +candidate's heads both `revision` verbs answer, the assigner's half named by its own revision and naming its inputs; a +placement change with no merge, and a `capabilities-changed`, each give a new assignment half on the same catalog half, +whose difference moves only the module concerned, and a part takes that pair as its view; after its first view a part +acts on a seat event without asking a verb again; a running part does not act on a candidate's view; a pull request +that adds a seat and a module using it is validated by the indexer's candidate pass and checked by the checker with +the same definitions; the tools of the table answer and the drop tools write the hand-act log. Whole (assigner): a +pull request's graph leaves every node's want unchanged; a placement-only revision moves the want of the module it +places; the builder and the grants part read their entries only through the seat `mesh-wants`'s verbs and events, and +the node-engine reads its want and the catalog half and never the assignment half. Whole (node-engine): a part started +while its node's want differs from what runs reports the build it was started with. Live: a search of the readers' +code finds none of the reads the revision view replaces, each by name: the indexer's read of the checker's previews, +the checker's watch of the indexer's `candidates.`, the forge bridge's read of the gate's `merges` and `owed`, +the checker's read of the gate's `source` and `turns`, the forge module's watch of the checker's `owed` (its `mesh/check` +status, now the checker's `owed` event performed by the forge bridge) and its read of the gate's `source`, the grants +part's read of the assigner's state `wants`, any module's read of the indexer's `modules` (its tool +`indexer_read_modules` in an `invokes`, its view in a `consumes`), the +checker's validator request with no seat definitions, a seat list compiled into the node-engine, the checker, the +builder or the grants part, a seat list's reader wired by a part itself, and a call of the forge module's own tools by +a delivery part or the gate; an old node-engine that wakes takes its new build in a form it reads. diff --git a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md new file mode 100644 index 00000000..5eab93ae --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md @@ -0,0 +1,408 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-10 +decisions: + - "ADR 0667" + - "ADR 0666" + - "ADR 0663" + - "ADR 0662" + - "ADR 0661" + - "ADR 0656" + - "ADR 0648" + - "ADR 0646" + - "ADR 0644" + - "ADR 0639" + - "ADR 0636" + - "ADR 0633" + - "ADR 0619" + - "ADR 0614" + - "ADR 0603" + - "ADR 0596" + - "ADR 0595" + - "ADR 0584" + - "ADR 0564" + - "ADR 0551" + - "ADR 0548" + - "ADR 0539" + - "ADR 0534" + - "ADR 0498" + - "ADR 0448" + - "ADR 0404" + - "ADR 0391" + - "ADR 0363" + - "ADR 0235" + - "ADR 0233" +--- + +# 47, milestone G: the acting parts, the removed hold, the check on the change's own acting parts, after the merge, taken in, and kept data on the bus + +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". + +## Order without a person: the acting parts + +### An acting part's new version acts first, a wait with a bound + +*ADR 0656, which supersedes ADR 0646; ADR 0603 §1 to §4, §8, §11, ADR 0614 §1, §3, §6, ADR 0633 +§1, ADR 0539 §7, ADR 0584 §1, §2, ADR 0564 §2.* + +§264 From research 085's milestone. A seat's definition may carry a rule, the step its holder acts in and the modules it +acts on, and a holder of such a seat is an **acting part** (ADR 0656): the indexer taking in and validating, the +checker checking, the build modules building, the node-engine composing, the grants part granting, the assigner +placing. *What ADR 0656 leaves to this design*, above, gives the rule's fields. Adding one needs its manifest and +its seat's rule, and no code elsewhere: the indexer's map from language to build module and the checker's own module +names go, since every new acting part needed code changes elsewhere (research 085, Findings 2). + +- §265 **The rule becomes edges between versions.** In the assignment graph the assigner gives an edge from the acting + part's version to each version its rule names, and no part names another module in its code to order by (ADR 0656). +- §266 **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. A bound's clock starts when the new version's build is done, as the builder's `build.ended` of that version says, its + time carried in `wait-held` as `clock-from`, 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; from G's delivery 30 for the runner's `pipeline`, whose wait ends when the new runner runs healthy, one module's apply on one node as composing's is (the design's choice under ADR 0666 §19, 2026-11-10). 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). +- §267 **A build module's wait ends on the agreed toolchain.** For building, an entry waits until the agreed toolchain of + its language (ADR 0584 §1) names the new build module, never merely until the new build module runs on one node: a + fingerprint names the agreed toolchain (ADR 0603 §3), so only then is the module built once, by the new one. A build + 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. +- §268 **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..` 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. +- §269 **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 + 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 the acting part's new + version, its bound and `clock-from`, and the watchman times it by that bound alone, raising no `pipeline-stands` for + a step held in 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 + 4; stopping the whole delivery would hold modules the failed part does not act on (the operator, 2026-11-06 17:0x). +- §270 **A wait holds only within its step, and skips the holder's own inputs** (ADR 0656). An entry waits only for its + own step's holders, so two acting parts that act on each other in different steps (the indexer taking in the + checker, the checker checking the indexer) form no cycle. Within a step a holder's own change is its first act, and + what its new version is built from or needs (a base, a package, a provider) is acted on by its running version, + never waited for: the running build module builds the shared contracts and the registry holder, then the new build + module builds the rest, so ADR 0614 §6's cycle through them stays allowed, now for that reason. +- §271 **The artifact store's holder takes its own new build even when its seat is the one broken.** A build module + stores the artifacts it builds through the seat `oci-artifact-registry`, so the + holder of that seat is in its own build's path: its new build needs the store it replaces. As every holder's own + inputs are (above), that store is acted on by the running holder: the running holder stores its new build, and only + then is the new one wanted. When the running holder is what is broken (the seat refuses or does not answer a store, + as in the hand bootstrap of 2026-11-07 to 11-09), the build module keeps that one build on its own node among its + kept builds (*The bootstrap delivery*: `build_go_kept`), says in its step key that it is kept on that node, and the + builder's done key names it so with its digest, which the want carries as it carries every artifact's digest; the + node-engine composing the holder fetches it through the build seat's verb `kept-artifact` of that node, and the new + holder, once it runs, takes the store of every build after it. + - The verb sits in the build seats' definitions (`node-build-go`, `node-build-node`, `node-build-image`, in + `mesh-delivery-contracts`), and its callers are the node-engines alone: the definition names them, and the grants + part grants it to no other account. + - It answers only for the module the seat list names as the holder of `oci-artifact-registry` at the running + revision, and refuses every other, naming the module, so a manifest that only claims the seat, and a store outage, + never turn into delivering from build nodes. + - The bytes go as the reply's object in the build module's sent store `_sent-objects` (*Any size*), never in + a value, since an artifact is over the bus's value limit; the build module is no foundation part, so it keeps no + old `objects` section, its kept data being in ADR 0595's templates (ADR 0666 §4), and the build itself stays among + its kept builds on its node. The sent store's cap is the objects template's; a holder image larger than its free + room refuses `kept-artifact` loudly, naming the store, as a full store does. The node-engine checks the bytes it + fetched against the digest its want names, never a builder's key (ADR 0596), and applies nothing that differs. + - A kept build that a step key names as kept is never swept until the module's `running` event names a later + fingerprint, which the build module hears through a durable consumer of the node-engine seat's events. Each build + seat reads and drops its kept builds by its own tools: `build_go_kept`, `build_node_kept` and `build_image_kept`, + with their `_drop`. The disk cost is one image of the store's holder on one build node (a guess: tens of MiB). + - While the keeping node is away, or its sweep removed the build, the want waits, and the node-engine says in its + have that it waits for that node or for a build no longer kept. A want that waits so for 60 minutes, building's + default bound, is the watchman's finding `kept-build-waits`, naming the module, the node and the build node; the + operator or the healers recover it by `builder_build_again`, which builds and keeps it again. + - A node-engine of before G does not know `kept-artifact`: the holder's want on its node waits for its new + node-engine (ADR 0667 §9). A new node-engine is itself fetched from the store; while the store is broken it waits, + so on a node that has neither the store's holder's new build nor G's node-engine, the want waits and + `kept-build-waits` names the node-engine's version as the cause, for the operator's hand at the forge as ADR 0667 + §11 has it. + + This is the one artifact the mesh delivers from a build node instead of the store, and only for the holder of + `oci-artifact-registry`; the design's choice under ADR 0656 §3 (a wait skips what the holder's own new version needs) and ADR + 0526 §2, whose build by a session's call it makes a step of the delivery instead of a hand act; the digest, the + holder check, the sweep, the bound and the old node-engine are the design's choices of 2026-11-10 (round 1). How it + is checked: test 60, and whole (build module, node-engine): a module claiming the seat but not its holder in the + seat list is refused; bytes differing from the want's digest are not applied; a want waiting 60 minutes raises + `kept-build-waits` and `builder_build_again` ends it. + + This is the one artifact the mesh delivers from a build node instead of the store, and only for the holder of + `oci-artifact-registry`; the design's choice under ADR 0656 §3 (a wait skips what the holder's own new version needs) and ADR + 0526 §2, whose build by a session's call it makes a step of the delivery instead of a hand act. How it is checked: + test 60. +- §272 **The check refuses a cycle among the waits within one step** (ADR 0656, which changes ADR 0603 §2's refusal of a + cycle), counted beside the edges ADR 0614 §3 and §6 list; a cycle through an acting part's edges across two steps, or + through a holder's own inputs, is none. +- §273 **The indexer's own steps have no wait, and a newer indexer is reached.** Taking in and validating stay as *Taken in + 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 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. +- §274 **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. +- §275 **An acting rule grants nothing.** A holder's bus grants come from the grants part as today (ADR 0539 §7). An + acting rule changes what a part acts on across every module it matches, so the operator's Approve ask of a change + names any acting rule it adds, and any it widens: a `step`, an `on` or a longer `bound` that now matches more + modules or holds them longer is named like an added rule. From G's delivery there is no Approve ask per merge, and + the pipeline's end names it in `widens` instead, refusing nothing (ADR 0666 §23). +- §276 **Each step's wait is its own part's, read and released by its own tools.** The part that does a step holds its + wait, so each wait has one owner that already holds that step's state, and says `wait-held` and `wait-ended` (*What + each step says*) with the step, the acting part, its version, the entry and since when: + + | Step | Holder | Reads its waits | Releases one | + |---|---|---|---| + | 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 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*) | + | pipeline, from G's delivery | the runner, holding the pipelines its new version is to run until it runs healthy | `runner_waits` | `runner_release` (run them with the running runner) | + + Each release writes the hand-act log of the seat `mesh-hand-acts`, with who released and why. +- §277 **How it is checked.** Whole (assigner): a fixture seat declaring a rule, with a fixture holder, gets its edges with + no code change elsewhere. Whole (assigner, builder, three build nodes): a group changing the shared contracts and the + build module that builds them, the new build module updated on the first build node first, builds no module with the + 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, + 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; a change to the runner holds its pipelines until the new runner + runs, raises `acting-part-late` past 30 minutes and no `pipeline-stands`, and `runner_release` moves them on; a + `wait-held` carries `clock-from` from the builder's `build.ended`. 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 + through a build module's edge each pass the cycle check, each new version first in its own step; a cycle among + waits within one step is refused. Unit (assigner): the Approve ask of a change that adds an acting rule, and of one + that widens one, names the rule. Live: a search of the delivery parts' code finds no other module's name. + +## Nothing merges into a repository under check: removed + +§278 **Removed at research 085's milestone.** The hold, the merge turns, their stalled line, the gate's tools for them and +the setting `check-gates-merge` go in milestone G, the gate's and the forge module's alike (the operator, on Telegram, +2026-11-09 01:11 UTC, choosing "Remove them in G"; the forge's merge then judges no check, *The seat `git`*). The setting has been false since 2026-10-25, so the gate wrote no turn and held no merge in that time, and the +operator's word for G is that no check gates delivery (1921's note of 2026-11-07). From G's delivery no merge waits on a check, and none on an Approve either (ADR 0666 §10): the checker is gone, and the +pipeline runs on the landed commit (*Pipelines per module*). **Its record is accepted:** ADR 0661 ends ADR 0548 §2 +to §4, §9 and §10 and ADR 0551 and changes ADR 0534 §4's judgement of `mesh/check` by the merge; until G's delivery the +rule below holds as those records have it. ADR 0662 is withdrawn; G builds ADR 0661 §1, §2 and §6, and ADR 0666 §10 +changes its §3 to §5 (the merge on the Approve alone), which G therefore builds for no merge. Beside it, ADR 0663 (accepted: the indexer answers `revision` in its own runtime to +exactly the parts that invoke it, the tool runner among them) holds the tool +runner's first view of the catalog half (*Every other reader of which modules exist*). How it is checked: in the gate's whole-module test an approved merge into a +repository under a running check is handed at once, and the gate's manifest declares no `turns` and no setting +`check-gates-merge`; in the checker's, a check claims at once with no gate state granted to read. + +## Checks: the check on the change's own acting parts + +### The check on the change's own acting parts + +*ADR 0656, which supersedes ADR 0648, ADR 0644; ADR 0448 §2, §4, ADR 0391 §1, ADR 0603 §10, ADR 0614 §7, ADR 0633 +§6, ADR 0636 §4, ADR 0639 §5, ADR 0498 §1.* + +§279 From research 085's milestone. ADR 0603 §10 and ADR 0614 §7 let a check pass while the part that would build or +check the change never ran: 7 of 24 failures since 2026-11-01 (research 085, Findings 4). The acting parts a change +alters, and the checking program, the rules every check applies, are built from the change's heads and check it; the +running versions never check in their place, and a check fails, naming the part, when one cannot be built or run +(ADR 0656). + +- §280 **Two tiers on the change's assignment graph.** The first tier builds, at the change's heads, only the acting parts + whose fingerprint in the change's assignment graph differs from the running one and which are to act on a module + the check acts on; a change to an ordinary module asks no first-tier build, since building every acting part for + every check would make an ordinary change pay for builds that change nothing. The second tier runs the check's + levels with those programs and with the revision view of the change's heads (ADR 0656). This keeps ADR 0603 §10's + building of changed build modules first and widens it to every acting part. +- §281 **The judging rules are a checking program built from the change.** Every check builds its checking program from + the change's heads and runs it with the revision view as its input; when it cannot be built or run, the check + fails naming it, and the running checker's own rules never judge in its place (ADR 0656). It is built once per + fingerprint and reused (ADR 0498 §1): checks whose heads leave the checking program unchanged ask one build between + them. +- §282 **Every program built from a change runs only in the build seat's sandbox.** The acting parts the first tier builds + and the checking program run in the run step, with no network (ADR 0448 §2) and no bus account, the logins staying + outside (ADR 0448 §4), through the seat `node-build-go`'s ask `run` (*What ADR 0656 leaves to this design*), as + the validator, the composer and the Go build module already run; a change not yet merged is not trusted. The running + checker alone asks, reads the program's verdict, records it and publishes it. +- §283 **A first tier that cannot build an acting part fails the check, naming it.** The verdict never passes on the + running version's word: a module whose changed build module the check cannot run is no longer composed from its + manifest (ADR 0656, which changes ADR 0603 §10 and ADR 0614 §7), and ADR 0614 §7's reason, a changed build module + 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`. +- §284 **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. +- §285 **ADR 0633 §6 stays, beside the change's own validator.** Whether the running parts can take the change in is + still checked with the trunk's manifest validator, built from the trunk (ADR 0633 §6); the change's own validator, + built in the first tier, judges the change's manifests besides (ADR 0656). A change fails if either refuses it. +- §286 **The asking and recording are covered by the checker's whole-module test**, which starts the changed checker on a + real bus and asks one check of a fixture group end to end (ADR 0391 §1). +- §287 **One seat list for both readers of a change's heads.** Inside every check, a lone pull request's as a group of one, + the checking program built from the heads makes one seat list from them, and the checker hands it to the manifest + validator and the composer (ADR 0644, ADR 0636 §4 with its insight of 2026-11-06); the running checker reads no + definition of the heads itself (*The bootstrap delivery, and the one milestone after it*). +- §288 **Its tools.** The checker's read of a check names the checking program's build that judged it, its fingerprint and + the heads it was built from, and the rules named changed; `checker_first_tier` lists a check's first-tier builds, + each with its acting part, its fingerprint and its outcome (`built`, `waiting for a build node`, `did not build`), + and asks one of them again. Asking a whole check again stays the checker's existing ask-again. +- §289 **What it costs.** A check of a change to an acting part costs one build per changed acting part, within the + checker's step bound (ADR 0639 §5); an ordinary change costs nothing more; a checking program costs one build per + fingerprint, shared by every check that uses it. +- §290 **How it is checked.** In the checker's whole-module integration test: a fixture change whose acting part does not + build, and one whose checking program does not run, each fail their check, naming the part; a checking program that + 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 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 + same checking program ask one build of it; a group that changes the assigner's placing is checked with its own + assigner; a fixture indexer change whose manifest the trunk's validator refuses fails its check, even when its own + validator passes it; a fixture group whose heads add a seat has that seat in the list both the validator and the + composer receive; the check's read names the checking program's build, heads and changed rules; `checker_first_tier` + lists a check's first-tier builds and asks one again; the changed checker asks one check of a fixture group end to + end. Live: the registry group passes its one check with its own checker, validator and composer. + +## After the merge, from milestone G + +- §291 **From milestone G's delivery, per watched branch.** The gate writes one source revision per watched branch, + `source.`, as a commit or a group lands there; the pipeline runs on each revision the indexer takes in, and + the wants follow a revision whose `required` steps passed, on the branch each placement chose (ADR 0666 §6, §9). +- §292 **From research 085's milestone, the wants follow the trunk's assignment graph.** A merge taken in makes a + catalog graph revision and then an assignment graph revision; the assigner writes each want from that revision, + its rules unchanged (ADR 0656), and each acting part's new version acts first in its own step (ADR 0656; *Two + graphs, one source of truth*, above). + +## Taken in by the indexer it names, from milestone G's delivery + +- §293 **From milestone G's delivery** (ADR 0666 §6, §13, which change ADR 0633 §1 and §3 to §6): before any take-in, on every + watched branch, the running indexer validates the revision with the manifest validator built from its heads, in the + sandbox, and a revision it refuses stays pending, as the bullets above have it, with the way back a revert; a pending + revision holds only its own watched branch; a group's member reaches it by landing, and the group ends when the gate's + group verb says every member has; `bridge_group_end_again` reads that verb; and the check's refusal below goes with the + check (*Watched branches and the catalog graph per branch*, *Groups*). + +## A backup before the apply: kept data on the bus + +### A module's kept data on the bus, backed up and restored with its delivery + +*ADR 0404, ADR 0235, ADR 0233, ADR 0539 §16, ADR 0595, ADR 0619 §2, ADR 0633 §1, §5, ADR 0363 §2; from research +085's milestone, this design's choice under ADR 0656.* + +§294 **ADR 0404's backup does not cover a module's kept data on the bus.** It backs up the items a moved module keeps on +its own node (ADR 0404 §2, ADR 0233), and the bus's streams are dumped whole by the bus module on its own rhythm +(ADR 0235), not per module and not before a delivery. A key-value state, a stream or an object store a module keeps on +the bus (ADR 0595, ADR 0619 §2), and its durable consumers' positions on the streams it reads, therefore have no +restore point taken before its module moves. That is a gap of the delivery system, and milestone G builds it, because +a revert of milestone G is the first change that needs it. It is built as the delivery system's capability for every +later change of a kept data's form, not as a case of this milestone. + +- §295 **Its form is declared.** Each kept data a manifest declares names a `form`, a number its holder raises whenever it + changes how it writes that data, or stops declaring it; a declaration naming none reads as form 0. The declaration + alone cannot tell a changed layout of the same bucket, so the holder says it. Only kept data whose `form` changes is + backed up and restored, so a part's other kept data, and every decision it holds, is never set back. +- §296 **It is backed up before the module moves.** When a want moves a module whose new manifest declares a kept data at + another `form` than the running one, or no longer declares it, the node agent applying it first asks the seat + `node-backup` of the node holding `mesh-broker` for a restore point of that kept data, and waits for its event + `ended`, as for any backup before the apply. The bus module's account takes it, as it takes the bus's snapshots + (ADR 0235 §5). A restore point is keyed by the module, the kept data, its form and the fingerprint it was taken + before, and it holds the kept data whole, the position of each durable consumer the module reads with, and the + catch-up tool the newer manifest declares beside that kept data. A failed + backup holds that module as any failed backup does. +- §297 **A revert restores it before the reverted build starts.** When a want takes a module back across a change of a + kept data's `form`, the node agent applying it asks that seat's verb `restore` for the restore point of that module, + kept data and form, waits for the seat's event `restored`, and only then starts the reverted build. The node agent + that restores is the one running when the revert starts. As every restore is, it is brought back beside the live data + and never over it: the live kept data of the newer form is set aside under a name of its own, and only the operator + removes it, so nothing written since is destroyed. The consumer's position is set back with it, so the reverted + build hears everything since as after any restart. +- §298 **The order of a delivery that crosses a form change** is the module graph's (*Order without a person*), with three + edges the assigner, which writes every want, keeps: + - the new versions of the `node-backup` holder and of the bus module run before any module whose kept data's form + changes is moved, since the restore point needs both; + - at a revert, a node's node-engine goes back last on that node, after every module it restores there has said + `restored` and runs, since the node-engine of before may not know how to restore; + - the reverted assigner and gate are wanted only once the reverted indexer has taken in the trunk's head, as its + `take-in.taken` naming the current source revision says (ADR 0633 §1), since the assigner of before computes the + wants from the indexer's `modules` and would otherwise write the fingerprints of the restore point. Until then the + running assigner writes no want, and every module keeps the fingerprint it runs. +- §299 **The restored part catches up forward, with the code it already has.** Read on main, mesh-delivery at 2d201f78 + and mesh-catalog at 1d07778c: + - **The indexer** (mesh-delivery, `modules/mesh-delivery-indexer`): its durable consumer `_` on the stream + `forge` redelivers every message since the restored position, and `handle` takes each + merge through `Indexer.Arrive` into `pending` and then into `modules` (`applyMerged`); `applySource` takes the + gate's `source.main` in, and `ApplySource` leaves a revision older than the latest it has seen. **The stream + `forge` keeps 7 days**: the forge bridge's manifest declares no age, and a stream's default is 7 days, at most 30 + (mesh-sdk `go/catalogue` `StreamDefaultDays`, `StreamMostDays`), so it does not cover the records' 90 days. A + merge older than the stream's first message is never redelivered, and the code then leaves that repository's + modules at the restore point's fingerprints. So the restore runs one forward step: once the reverted indexer + runs, the node agent that restored it calls its tool `indexer_take_in` with each repository the gate's source names + (`NewTakeInRepository`, which takes in the head the source names for a repository). The catch-up tool is declared + by the newer build's manifest, beside the kept data whose form it changes, and recorded in the restore point when + the backup is taken; the node agent takes it from the restore point, never from the manifest being applied, since + at a revert that is the manifest from before the change, which names none. So the delivery system names no + module's tool. + - **The assigner** (mesh-delivery, `modules/mesh-delivery-assigner`): it keeps nothing restored. Its pass + (`passer.wants`) writes, from what the world holds now, each node's want by the six rules from the indexer's + newest fingerprint at the trunk, and only where the value differs; so once the indexer has caught up it writes + the trunk's wants, and nothing older. + - **The gate** (mesh-catalog, `modules/mesh-delivery-gate`): `Holder.Load` reads the restored deliveries and groups + back; `readOnStart` then reads what happened while it was away: `wantForge` reads the forge's open pull requests + at the next look, `hearAsks` reads the teller's answer to every ask it still holds as pending, and + `SourceCatchUp` brings the newest merge of each repository into `source.main`, never moving a repository back. An + ask the gate of milestone G raised after the restore point is not among the pending ones, so its answer is not read + and the operator is asked again. Whether the first look clears a merge kept in the restored `merges` that the forge + merged since was not read in the code; the catch-up test below proves it with the real builds. +- §300 **A missing restore point holds the revert, naming it.** When the restore point a revert needs does not exist, or + `restored` does not come, the node agent applies nothing of that module, keeps what runs, and writes the have phase + `held` with the reason `restore point missing` and the module, kept data and form it names; the assigner asks the + operator (hold, or start the reverted build on the live data as it is), raises the condition naming them, and on + the answer writes the node's want again naming it (ADR 0539 §16). +- §301 **What the operator is told.** A revert across a change of a kept data's form puts that kept data back to the + moment before the change moved it: the deliveries made since stay delivered, but what the part wrote there since + (for milestone G, its delivery records and its graph revisions) is set aside and not read again; an Approve asked + and answered since is asked again, and one the gate asked before still reads its answer. The revert's Approve ask + says this in those words, naming each kept data restored. +- §302 **Its tools.** The seat `node-backup`'s `backed-up` lists each module's restore points of its bus data, with the + kept data, the form and the fingerprint each was taken before, and the data set aside at each restore, with its + size; `restore` restores one; `discard-aside` removes data set aside by a restore, only on the operator's call, and + writes the act, who and why in the hand-act log of the seat `mesh-hand-acts`. `backed-up` and `restore` exist on + the seat today and gain the module's kept data on the bus as an item; `discard-aside` is new. +- §303 **How it is checked.** Whole (node-engine, backup holder, bus module): a want moving a module across a `form` + change, and one moving a module whose kept data declares no `form` to one that does, each take a restore point + first, keyed by module, kept data, form and fingerprint; a want taking it back restores it beside the live data and + the consumer's position before the reverted build starts, and the data set aside is listed with its size and + removed only by `discard-aside`, which writes the hand-act log; a revert of a group holding the node-engine and a + module whose form changes restores the module before the node-engine goes back; a revert whose restore point is + missing is held, naming it, and moves on only on the operator's answer. The catch-up is proved with the real builds + of the indexer, the assigner and the gate from before milestone G, never fixtures (acceptance test 36). diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md new file mode 100644 index 00000000..067b048e --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md @@ -0,0 +1,478 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-10 +decisions: + - "ADR 0668" + - "ADR 0667" + - "ADR 0666" + - "ADR 0660" + - "ADR 0659" + - "ADR 0656" + - "ADR 0635" + - "ADR 0625" + - "ADR 0619" + - "ADR 0614" + - "ADR 0603" + - "ADR 0596" + - "ADR 0595" + - "ADR 0564" + - "ADR 0563" + - "ADR 0551" + - "ADR 0548" + - "ADR 0534" + - "ADR 0493" + - "ADR 0443" + - "ADR 0441" + - "ADR 0398" + - "ADR 0363" +--- + +# 47, milestone G: the verbs G adds, what retires, and the answers to the builders' and the reviewers' questions + +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". + +## The verbs milestone G adds + +§304 From research 085's milestone the seat gains: + +| Verb or event | Answers or does | +|---|---| +| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656), and the acts the gate said it owes the forge for it, each with its id and its recorded outcome (*What the gate owes the forge*) | +| `owed` | an act the gate owes the forge (a merge, a status, the delivery's view, a comment, a line on the commit's note), with its pull request, head and id, on `mesh.seat.mesh-delivery.event.owed..`, said only from the gate's account and heard by the forge bridge through a durable consumer (*What the gate owes the forge*) | +| `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | until G's delivery: the one Approve ask per milestone (ADR 0493 §9 as ADR 0659 §1 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.`, the milestone in the 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`. G's delivery ends the ask and its three events with the Approve per merge (ADR 0666 §10) | +| `record-opened` | a delivery record opened, which the watchman times for `record-open` | +| `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`, `room` | a delivery record ended; the ledger past 80 % of its cap; the ledger full; the ledger back below its cap (*The delivery record*) | + +§305 From milestone G's delivery the seat's `table`, `recheck` and `close` go with the merge states (*The state table*), +the milestone ask and its three events with the Approve per merge, and it gains (ADR 0666 §6, §26): + +| Verb or event | Answers or does | +|---|---| +| `source` with a watched branch | that branch's source revision; the indexer and the node-engine's catch-up read it, never the gate's state | +| `source-moved` | a new source revision of a watched branch | +| `recovery` | marks a merge its author names a recovery (the commit and why), its record marked `recovery` with the caller the runtime proves | +| `record-opened`, `recovery-merged` | a delivery record opened; a merge named a recovery, which the teller passes on to every channel | + +§306 **The seat `mesh-pipeline-runner`**, from milestone G's delivery, defined in `mesh-delivery-contracts` with the callers +of each verb (ADR 0666 §7, §20, §25): + +| Verb | Answers or does | Callers | +|---|---|---| +| `run` | the run on request of one or more members, each a module at a named commit; delivers nothing | the sessions (the agents' bus identities the tool runner proves) and the operator, nothing else | +| `start` | a delivering pipeline for a watched branch's head, by hand | the operator, the healers | +| `show`, `list`, `stands`, `why-carried`, `kept` | one pipeline whole with its step log; the pipelines and runs of a module, a branch or a commit; what stands still; where a result was carried from; the kept results | any person, an agent session, the healers | +| `review` | a code review's result for a commit and fingerprint | the reviewers the definition names; refused from the commit's author | +| `rerun`, `accept`, `cancel`, `ask-again` | one step again; a failed `optional` step accepted with why; a pipeline ended, its asks dropped; a declined `approval` asked again | the operator, the healers (`accept` and `ask-again` the operator) | + +§307 **The seat `mesh-catalog`** gains, from milestone G's delivery, `register`, `policy`, `watch`, `unwatch` and +`registrations`, and the event `registration-changed` (*A module's registration*), and `catalog-revision` names its +watched branch in its subject. **The build seats** gain the verb +`steps` and the accepted kind `step`, and **the seat `mesh-wants`** the kind `step`, each saying `step-ended`. **The +seat `mesh-builds`**, new and held once for the mesh, says `package-published` (*A publish rebuilds*). + +§308 **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 +each step says*. + +§309 **The seat `node-engine`**'s event `running..` carries, from research 085's milestone, the module, its +fingerprint and the digest of its build's manifest, so the node-engine of another node and the grants part decide a +seat's pass from it (ADR 0660 §2; *Who holds a seat is applied by one rule*). No verb is added. + +§310 **The seat `git`.** The seat `git` (design 26, *The git seat*), of the Delivery domain, defined in +`mesh-delivery-contracts`, holds no verb today, and the delivery parts and the gate reach the forge by naming the forge +module's own tools and events and guessing the shape of their answers (on 2026-11-09 one guess, an answer wrapped in a +`result` field, made 316 conditions stand, issue 1966). From research 085's milestone the seat's definition carries the +verbs and events below, their inputs and answers typed in its contract, served and said by the module that claims the +seat; every delivery part and the gate call and hear the seat, never the forge module by name (acceptance test 27). +They are this design's choice under ADR 0596 §1, §2 (a module's events are subscribed to and its data asked through +its seat verbs) and ADR 0625 §1 (a seat's contract is generated from its definition). The table is built from what the +parts' manifests declare in `invokes` and `consumes` and what their code calls, read on main on 2026-11-09 (mesh-delivery +042b8d1f, mesh-catalog's gate); a declaration nothing calls (the checker's `gitea_commits`, `gitea_compare`, +`gitea_list_branches`) gets no verb and is dropped from its manifest: + +| Verb | Answers or does | Called by (today's call) | +|---|---|---| +| `pull` | one pull request, by its repository and number or by its merge commit: number, base, head branch, head, merge commit, author, state, and `merging` while the forge merges it; an unknown one answers the error kind `not-found` | the forge bridge (a merge the gate did not make; `gitea_api`), the gate (a stalled line's author) | +| `branch` | from milestone G's delivery: a branch's head, and whether a protection rule refuses a direct push and admits only merges | the indexer (`register`, `watch`), the forge bridge (a watched branch's head) | +| `open-pulls` | every open pull request of every repository, its head and base, the statuses its base requires and those its head has | the forge bridge at its start and each reconnect (ADR 0443 §5), the indexer, the gate (`gitea_open_pulls`) | +| `tags` | a repository's tags, paged, the page's bound said when more exist | the forge bridge, for the builder's floor (`gitea_api`) | +| `trees` | the trees of one commit | the forge bridge, the checker, the indexer (`gitea_commit_trees`) | +| `file` | one file at one commit | the forge bridge, the checker, the indexer (`gitea_get_file`) | +| `contains` | whether one commit contains another | the indexer, the gate (`gitea_contains`) | +| `statuses` | the statuses a commit carries | the gate (`gitea_commit_statuses`) | +| `status`, `comment`, `view`, `note` | a commit status, a comment, the delivery's one view comment, a line on the commit's note under `refs/notes/mesh-plan`; a comment and a note line carry the act's id and an id already held is skipped | the forge bridge alone, performing the gate's and the checker's `owed` (`gitea_commit_status`, `gitea_add_comment`, `gitea_delivery_view`, `gitea_note_append`) | +| `merge-pull` | until G's delivery: merges a pull request at the head the operator's Approve names; refuses a head that moved, a branch no rule guards, a head the Approve does not name and a merge git refuses; judges no check (below). From G's delivery: merges with the forge tools' account (ADR 0398 §2) at the head the caller names, judging that it is still the pull request's head and that the branch's protection refuses a direct push, the protection read again on every `branch-moved`, and refusing a head when any run at its build-input fingerprint failed a `required` step, judged from its own state `failed-steps` heard from the runner's `step-ended`, never by a call; a head with no run yet or a passed one merges, it never waits for a run and never asks an Approve (ADR 0666 §11). A pull request already merged at that head is answered performed | until G's delivery the forge bridge alone (`gitea_merge_pull_request`); from it any session the tool runner proves, and the operator | + +| Event of the seat | Says | Heard by (today's event) | +|---|---|---| +| `pull.updated`, `pull.merged`, `pull.closed` | a pull request's new head, its merge with the merge commit, its close | the forge bridge, the gate (`gitea.pull.*`) | +| `forge.pull`, `forge.status`, `forge.delete` | the forge's own webhook of a pull request, of a commit status, of a deleted branch | the gate (`forge.pull`, `forge.status`), the forge bridge (`forge.delete`) (`gitea.forge.*`) | +| `repo.writable` | a repository the forge can write again | the gate (`gitea.repo.writable`) | +| `branch-moved` | from milestone G's delivery: a branch's new head, by a merge or an admitted push, from the forge's push webhook | the forge bridge | + +§311 Each is said on `mesh.seat.git.event.`, declared in the seat's definition, and heard through the hearer's +`consumes` of the seat. + +- §312 **A failed read answers an error, never an empty answer.** When the forge does not answer, refuses or answers what + the contract cannot type, the verb answers an error naming why; an empty list or an absent file is answered only + when the forge said so. A caller never reads an error as "none". How it is checked: the seat's fixtures hold an + error answer for each verb, and each caller's unit table has a row for it (the forge bridge, the checker, the + indexer, the gate). +- §313 **Who may call an act.** The holder's trusted config value `callers`, which today names per verb who may call the + two package seats' verbs (design 51), gains a line per verb of the seat `git`: the act verbs name the forge bridge + alone, the reads name each part that declares them in `invokes`; the grants part grants by it, and the holder + refuses a caller it does not name. From G's delivery the callers per verb are the seat definition's field `callers`, + the grants part's one source (ADR 0666 §20): `status`, `comment`, `view` and `note` the forge bridge alone, + `merge-pull` the persons and agent sessions the tool runner proves, the reads the parts that declare them. The forge module's own acting tools (`gitea_merge_pull_request`, + `gitea_commit_status`, `gitea_delivery_view`, `gitea_note_append`, `gitea_add_comment`) retire with the milestone, + and so do its watch of the checker's state `owed` and its read of the gate's `source`, so the bridge stays the one + part that acts on the forge. How it is checked: test 51 reads the grants, not the code. +- §314 **`merge-pull` judges no check, and from G's delivery no Approve, only the runs at the head's fingerprint.** The operator's answer that no check gates delivery (Telegram, 2026-11-09 01:11 UTC, + "Remove them in G") covers the forge module's own setting `check-gates-merge` as well as the gate's: both are + removed, and `merge-pull` merges an approved head whatever its `mesh/check` says. The record that ends ADR 0551 and + ADR 0548 §2 to §4, §9 and §10 also changes ADR 0534 §4's judgement of `mesh/check` by the merge; it is to be accepted + before the push (*Nothing merges into a repository under check*). From G's delivery it judges no Approve either, its caller + is any session the tool runner proves, and it refuses a head when any run at its fingerprint failed a `required` step, + never waiting for one (ADR 0666 §11; *Approval is a step, and a session merges a pull request*). The reads the + gate would have needed to merge (the forge's word for a conflict, the base's required statuses, the merging account's + write access) are not added: no part of the mesh but `merge-pull` merges, and it answers git's own refusal of a merge + as refused. +- §315 **The order is the module graph's, and each step has its way back.** The verbs are a served-verb edge (ADR 0614): + the forge module's new build runs before the forge bridge's, and the forge bridge's before the gate's, since the + bridge calls the verbs and performs the gate's `owed`, and the gate says `owed` only to a bridge that hears it; all + three are ahead of the acting parts' steps, which read through the seat. While the new forge runs and the old bridge + still calls the retired acting tools, the old bridge's acts fail loudly and stay unperformed in the old gate's keys, + and the switch carries them (*What the gate owes the forge*). A revert goes back in the other order: the gate, then + the bridge, then the forge, each wanted only once the one after it in the forward order runs its reverted build, so + no part calls a verb nobody serves. ADR 0656 §3 stands beside it: the acting parts' new versions act first in their + own steps. Two things cover the gap while the forge is still on its old build and serves no verb of the seat: the + indexer starts on its kept definitions (below), and a check whose read through the seat `git` fails says `could not + run`, naming the forge, and is asked again on the forge module's `running` (*A check runs to its end*). +- §316 **A part that cannot read the forge keeps what it has, and says so.** The indexer, at its start, waits for the + seat `git` at most 2 minutes; with no answer it judges from the last definitions it kept, says in its + `_first_views` that it waits for `git` and since when, writes `view-waits` after five minutes, and reads the + definitions once the seat answers; it then judges again every manifest it judged from kept definitions. Two minutes is longer than a forge's restart, and short enough that a start is + not held behind a forge down for good. How it is checked: test 54. +- §317 **How it is checked.** The seat definition's conformance fixtures hold one answer and one error of each verb and one + of each event, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's, the + indexer's and the gate's unit fakes answer from the same fixtures; a call of an act verb from an account other than + the forge bridge's is refused (test 51); live, a search of the delivery parts' and the gate's code finds no name of + the forge module's tools or events. + +§318 **Links of the trace.** No part reads a review line (ADR 0659 §1 to §3); who may review and the review verb's grant +stay issue 1782's, outside milestone G, and that verb gates nothing (ADR 0659 §2). The trace's links are this +design's choices under ADR 0656, so that one call traces research to the running build +with no text match (research 085, Findings 7). The issue tracker gains link kinds from an issue to the decision and to the research effort +it carries, made by a tool call and never by text, as ADR 0493 §4 has every link made; the knowledge base gains a +typed link from a decision to the milestone of the issue tracker that builds it, made by a tool call, which joins the +link kinds of its link-fits rule (design 55), beside the `became` link a research effort gets at its graduation +(playbook 02). How it is checked: whole (gate), one ask per milestone, withdrawn by a new head; +a waiter on one member's pull request wakes only on that member's transition. Whole (issue tracker, knowledge base): +an issue linked to a decision and to a research effort reads both back; a decision linked to its milestone reads it +back. Live: research 085's milestone's record reads back research 085 to its decisions, its milestone, its issues and +its pull requests with no text match. + +§319 **A session hears that a record was accepted** (issue 595). The knowledge base already says, on its holder's own +stream, the event `reviewed` for every judgement of a record; from milestone G that event carries, when the operator's +Approve accepts a record, that it was accepted and by the operator, with the warrant, and the tool runner's account is +granted to hear it, so a session waiting on a record wakes on it. The design's choice under ADR 0596 §1 (a module's +events are subscribed to): a new event of the seat `knowledge-base` would be a second path for the same fact (lead-47, +2026-11-09). How it is checked: in the knowledge base's whole-module test an Approve that accepts a record says +`reviewed` with what was accepted and by whom, and the tool runner's account hears it. + +## What retires at milestone G + +- §320 At research 085's milestone's delivery: the indexer's map from language to build module, and the checker's own + module names and its cases for a change to the manifest validator and to the Go build module (ADR 0656); a build + module's edge as a trigger, with the extra build per recipe change (ADR 0603 §3, as ADR 0656 changes it); composing + from manifests a module whose changed build module the check cannot run (ADR 0603 §10 and ADR 0614 §7, as ADR 0656 + changes them); the module graph's one key per module with five past values as what parts read (ADR 0603 §2, as + ADR 0656 changes it); the tiers worked out from five live sources (ADR 0603 §6, as ADR 0656 changes it); a seat + list compiled into a part, and every read of another module's bus state the revision view replaces (ADR 0656); the + gate's `merges`, carried into the delivery records and written no more, and its `owed`, said as events; the hold, + the merge turns, their stalled line and tools and the setting `check-gates-merge`, once their record is accepted; + the last three reads of a pull request's description (ADR 0659 §4); every delivery part's call of the forge module's + own tools, for the seat `git`'s verbs; and the four seat-list readers the delivery parts wire by hand. +- §321 At milestone G's delivery, from research 086 (ADR 0666, ADR 0667): the checker (`mesh-delivery-checker`), its seat, + its try with `tryevent` and the indexer's try candidates, its kept verdicts and carries (the runner's from then), the + checking program and `mesh/check`, and its buckets and bus account, removed with the gate's forge merge token only + after the first pipeline after G's delivery has delivered a change end to end and on the operator's one word, its + buckets through `drop` after a snapshot whose restore is tested (ADR 0667 §12); the bundle of steps run before a merge; the Approve per + merge, the milestone ask and its three events, and the gate's merges; the gate's merge states, one key per pull request and its verbs `table`, `recheck` and + `close`; the trunk-only want; `source.main`, for one source revision per watched branch; every fixed branch, + repository and module name in code logic; the recovery merge verb this design sketched, never built; and the holders' + trusted `callers` values, for the seat definitions' `callers`. The words **merge check**, **try**, **trunk commit** + and **merge turn** go to *Not:* lines with the as-is update after that delivery, as the glossary's entries say. + +## What milestone G's builders found open, answered by this design (2026-11-10) + +*ADR 0666, ADR 0667, ADR 0668; ADR 0656, ADR 0596, ADR 0595 §4, §9, ADR 0619 §1, §2, ADR 0563, ADR 0564 §2, ADR 0625 +§1, §4, ADR 0635 §4, ADR 0548 §5, ADR 0363 §2; design 27, *Which module answers*. The operator, 2026-11-10: a small +choice within what a record decides is the design's own, written here with its reason and reviewed by the reviewers +by architectural domain.* + +§322 Milestone G's prepare and develop met these questions (issue 1966). Each line is the design's choice, with the +record it follows from, and replaces the builder's own choice where one was made. + +### The runner and its steps + +- §323 **A step's build seat is read from the artifact's language.** The catalog graph entry names each artifact's + `language`, as its manifest's artifact entry says it and as the indexer's language map matches today; the runner + reads it from the catalog half of its revision view and asks lint and the test steps of the build seat whose + definition's acting rule `build` has `on` naming that language. With no such seat, or no holder of it, the pipeline + ends failed, naming the seat (ADR 0666 §18). Why: the language is already in the manifest and the catalog half is + the runner's one view (ADR 0666 §24, ADR 0596). How it is checked: whole (runner), a Go and a TypeScript fixture + module each ask their own build seat, and one with no holder ends failed naming the seat. +- §324 **An acting rule's `on` may name an artifact kind.** Beside `every` and a language, `on` may be `kind: image`, + matched against the manifest's artifact entry's kind; the seat `node-build-image`'s rule says `kind: image`. Why: an + image has no language, and the rule's fields are this design's (*What ADR 0656 leaves to this design*). How it is + checked: a unit row of the definition reader accepts `kind: image` and refuses another kind. +- §325 **An approval never asked ends `not asked`.** When a `required` step failed before the `approval` step was asked, + the `approval` step ends `not asked`, naming the failed step, never as passed; a `rerun` that passes the failed step + asks it then, and the pipeline ends again: the runner says `pipeline-ended` once more for it, its end count one + higher, which the assigner takes (*What holds a delivery*). Why: approval comes last (above), + and every end a step can have is said as itself (ADR 0666 §10). How it is checked: whole (runner, assigner), a failed + unit step leaves the approval `not asked`; its passing rerun asks it, and once approved the second `pipeline-ended` + writes the want. +- §326 **A build seat keeps its `steps` answer.** The build module keeps each artifact's detected steps in its state + `steps`, from the template `state`, one key per artifact holding the answer for that artifact's newest build-input + fingerprint, replaced when a newer fingerprint is detected and by `steps` with `again`; a key whose artifact no + longer exists is dropped by `steps` with `drop`, on a person's word, written in the hand-act log. With one key per + artifact (about 120) the state needs no aging and no read time. Why: ADR 0666 §25 asks a tool for "the steps + detected for an artifact and detect again", which a reply computed anew each time would not need; a build module is + no foundation part, so the template form (ADR 0666 §4). How it is checked: whole (Go build module), `steps` answers + from the key, a newer fingerprint and `again` replace it, and `drop` removes one. +- §327 **The step code lives in the domain SDK.** The step run and the fetch-and-release code the three build modules + share move, in this milestone, into a build-step package of `mesh-delivery-sdk`, which the Go, TypeScript and image + build modules import; none keeps a copy. Why: code shared between modules lives in an SDK, and the delivery + domain's shared code is `mesh-delivery-sdk`'s (ADR 0635 §4); the copies were met in develop. How it is checked: a + search of the three build modules finds no fetch, release or step-run function of their own. +- §328 **Lint per language.** Go's lint step is the Go build module's linter with the fixed-names rule; TypeScript's is the + type-check with the fixed-names rule; an image's lint is the fixed-names rule over its build files; + an image has no unit or integration test step, so `steps` answers none for it and the seed has those `bypass` + (*A module's registration*). Why: ADR 0666 §5 has the lint step refuse fixed names in every language, and §7 lets a + build seat answer only the steps an artifact can run. How it is checked: whole (TypeScript and image build modules), + a fixture naming a module in code fails lint. +- §329 **What the fixed-names rule exempts.** It exempts a Go import path, a comment and a test file (`_test.go`, + `*.test.ts`), never a struct tag or any other string of code. Why: none of the three is code logic (ADR 0666 §5), + and a test fixture names modules. How it is checked: unit (lint rule), a name in a comment and in a test file + passes, in a struct tag fails. +- §330 **The runner's own kept data age by its own deletes, in the step log's order.** The runner deletes its own + `pipelines` keys, `kept` entries and `step-outputs` objects by the retentions of *The runner's kept data*. It keeps, + in its old `state` section `aged`, three step-log sequences, one per retention (7, 30 and 90 days), each the last + entry it aged at that retention. At each step's end it reads the step log onward from each sequence, at most 20 + entries per sequence, while the entry is past that retention: at 7 days it deletes the entry's output and the + pipeline it names unless that pipeline ended passed on a watched branch or is its branch's newest for its module; at + 30 days each `kept` digest the entry names whose `last-read` is past 30 days; at 90 days the passed pipeline the + entry names, unless it is its branch's newest. It then moves the sequence; nothing is scanned whole. Each delete + writes a line in the runner's journal naming the key or object and its retention. A foundation part's sent store + (`sent-objects`) is aged the same way by its owner at each send, from the sequence of its own sends. Nothing waits on + a timer, and nothing but the owner deletes. Why: an old section cannot declare an age, and deleting one's own entries + is the owner's act, not the removal of a store ADR 0595 §4 holds to a person's word; §57's rule that only the drop + tool removes an object holds for a template's `-objects`, while the runner's `step-outputs` are its own old + section's objects. How it is checked: whole (runner), an output 7 days old is gone after the next step's end, a + passed pipeline stays to 90 days, and one step's end deletes at most 20 per sequence. +- §331 **The kept event streams' caps, and when one is full.** On the seat `mesh-pipeline-runner`, each kept 90 days: + `pipeline-started` 32 MiB, `pipeline-ended` 192 MiB, `group-ended` 64 MiB, `run-started` 16 MiB, `step-started` + and `step-ended` 128 MiB each, `wait-held` and `wait-ended` 32 MiB each; the seat `git`'s `branch-moved` 64 MiB; each + build seat's `step-ended` 128 MiB; `mesh-wants`'s `step-ended` 32 MiB; `mesh-builds`' `package-published` and + `mesh-catalog`'s `registration-changed` 16 MiB each; all 90 days. Why: about 300 pipelines a day of about 6 steps + (*The runner's kept data*) say about 1,800 step events a day of about 0.5 KiB, about 80 MiB in 90 days, and about 300 + pipeline ends of about 4 KiB, about 105 MiB, 55 % of its cap; a build seat runs about 1,200 steps a day, about 54 + MiB; publications and registration changes are a few a day; estimates, read from the fill tools once G runs. A kept + seat stream at its cap drops its oldest message, as a stream with a cap does; the grants part, which makes the + streams, says `filling` on its seat `mesh-grants` past 80 % and `room` below 70 %, which the watchman's + `store-filling` raises and clears as for every store (*A store filling is said before it is full*). How it is + checked: each seat definition's fixtures name each stream's `keep`; whole (grants part), a stream past 80 % says + `filling`. +- §332 **A group's end is said under the group.** `group-ended` is said on + `mesh.seat.mesh-pipeline-runner.event.group-ended..`, with no module part, since a group spans + modules; a session that merged a group's member waits on `group-ended..` of its group after its own + `pipeline-started` (*What a session hears after its merge*). Why: every other runner event names one module + (*Health and going back*). How it is checked: the seat's + fixtures. +- §333 **The runner hears the indexer's own event.** The runner's manifest `consumes` the indexer's own event + `take-in.taken` (ADR 0666 §13), which ADR 0596 §1 lets any module subscribe to; the indexer's name stands in that + manifest only, never in the runner's code. Why: ADR 0666 §13, in force, names that event. How it is checked: a search + of the runner's code finds no module name (lint). +- §334 **A publication's pipelines and their key.** `package-published` carries the package's artifact reference + (`:`, from the catalog entry), its version and digest, the watched branch and the source revision + its build was made from. The runner reads the dependents from that revision's catalog graph, the artifacts with a + kind-1 edge to the package, and starts each one's pipeline keyed by that branch, that source revision and the + dependent module, the version in the pipeline's state; a `take-in.taken` of the same revision and module is the + same pipeline (*A second start writes nothing*). Why: a pipeline's key is branch, source revision and module (above), + and the publication is made from a revision of a watched branch (ADR 0666 §15). How it is checked: whole (builder, + runner), a publish starts one pipeline per dependent, keyed by the publishing revision. +- §335 **What the teller says of a widening.** Each entry of `widens` names what widens (`acting-rule`, `grant`, + `credential` or `serving-part`), its name, the module and the commit. The teller says one message per + `pipeline-ended` that carries any, on every channel, once per module, watched branch and commit: it keeps in its + state `widens-said`, from the template `state`, one key per module and watched branch holding the last commit it + said, replaced by the next and deleted when `registration-changed` says the branch is no longer watched; its tool + `teller_widens_said` reads it. Why: ADR 0666 §23 + says a widening in the pipeline's end, and the teller passes it on (*Who may say what*). How it is checked: whole + (teller), a `pipeline-ended` said twice with the same `widens` gives one message. + +### Callers + +- §336 **The forms of `callers`.** A verb's or an accepted kind's `callers` is a list whose entries are: a holder role (a + seat's name), a module, `operator`, `healers`, `persons` (any person the tool runner proves), `sessions` (an agent + session the tool runner proves), `invokers` (every module whose manifest declares the verb in `invokes`), and + `holder:@same-node` (the holder of that seat on the node of the verb's holder). A definition whose `callers` + today is text is given a list in this milestone: "any caller whose role grants it" becomes `invokers`, "a person" + `persons`, and every other text the names the design gives that verb. Why: ADR 0666 §20 makes the definition the + grants part's one source of caller limits, in a form it reads. How it is checked: unit (definition reader), each + form accepted and any other entry refused; the grants part's table test grants each form. +- §337 **The bus module's restore points.** The verbs the holder of `node-backup` on the bus module's node calls to take + and restore a module's kept data on the bus name `holder:node-backup@same-node`. Why: only that holder takes them + (*A backup before the apply: kept data on the bus*). How it is checked: a call from another node's `node-backup` + holder is refused by the bus. +- §338 **Callers on accepted kinds.** An accepted kind of a work queue carries `callers` as a verb does, and the grants part + grants publish on its subject to them alone: the build seats' `step` the runner; `run` the runner, the indexer and + the assigner, and the checker until its entry is removed with its buckets and bus account (ADR 0667 §12); `mesh-wants`'s + `step` the runner. Verbs: `steps` the runner and the indexer (its seed); `drop` the runner and the builder; both + `input` verbs (`mesh-catalog`'s and `mesh-wants`') the holders of the build seats. Why: a step's result is taken only + from its seat holder (ADR 0666 §21), so the ask side is limited too. How it is checked: a `step` published by another + account is refused by the bus. +- §339 **The contract generator reads `callers` and makes nothing of it.** The generated contracts carry no caller check; + the grants part reads `callers` from the definition, and the generator refuses a definition whose entry is no form + above. Why: one source of caller limits (ADR 0666 §20). How it is checked: the generator's test refuses an unknown + entry. +- §340 **The reader release first.** mesh-sdk's seat-definition reader released before G's check reads `callers` as text + or as a list; G's own reader refuses text. Why: readers first (ADR 0667 §4), and every definition is a list from G; it is merged ahead of G's check because the + running checker, which makes G's own check (ADR 0667 §2), must read G's definitions, the operator's choice of a + reader first (2026-11-09). + How it is checked: unit rows for both forms in the release, and for the refusal in G. +- §341 **TypeScript contracts as JavaScript.** The generator emits each seat's TypeScript contract as `index.js` with its + `index.d.ts`, so a TypeScript module imports it with no build step of its own. Why: a seat's contract is generated + from its definition (ADR 0625 §1), and a gap of the build system is built in it. How it is checked: a TypeScript + module builds against a generated contract with no `tsc` of the contract. + +### The catalog, the builder and the assigner + +- §342 **A module not watched on a branch.** A watched branch's catalog graph takes each module not watched on that branch + from the registration's first watched branch. `register` names a module's watched branches; with none named it takes + the repository's default branch, read through the seat `git`. Why: a placement's default is the registration's first + branch (ADR 0666 §6). How it is checked: whole (indexer), a `beta` revision holds a `main`-only module at `main`'s + head. +- §343 **Branch names the indexer's keys use.** `register` and `watch` refuse a watched branch named `candidate` or + `entry`, naming why, since the catalog keeps its revisions under `.` beside those two key forms. + How it is checked: whole (indexer), each is refused. +- §344 **The bus module plans a sent store from what the catalog already says.** It plans `_sent-objects` for a + module of the new form whose catalog graph entry claims a seat whose definition has a verb or an event, or declares + an event of its own, read from the catalog half of its revision view and the seat list; the indexer's event carries + no field `sends`. When a seat's definition gains its first verb or event later, the bus module plans the store for + its holders on the seat list's change. Why: no manifest field (*Any size*), and the catalog half is every part's view + (ADR 0656 §6). How it is checked: whole (bus module), a module claiming a seat with a verb gets a sent store, one with + neither gets none, and a definition gaining a verb gives its holder one. +- §345 **A large value reaches the tool runner in chunks.** A bundle's put of a value over the bus's per-message limit is + sent to the tool runner as a put session on the bundle's call subject, named by the call's id, in chunks under that + limit, in order, each answered by the tool runner before the next is sent, and written as one object through the + object store's own chunking; a put not sealed within the call's bound is dropped by the tool runner and its call + fails, naming the object. No value is split over objects. Only modules that import the object store package are + rebuilt for it. Why: one object per value (ADR 0666 §16), through the tool runner (ADR 0595 §9). How it is checked: + whole (mesh-sdk), a value of a hundred times the limit is read back whole, and a put stopped half way fails naming + the object and leaves none. +- §346 **One object store package.** The assigner's and the indexer's object store code is mesh-sdk's small object store + package, with no copy in either. Why: ADR 0595 §9. How it is checked: a search finds no second copy. +- §347 **Reading sent objects is a bus state call.** mesh-sdk lists the call that reads a sent object under its existing + bus state capability. Why: ADR 0595 §9 puts the calls in that package. How it is checked: the package's table test. +- §348 **The builder reads the catalog through its seat.** The builder reads the registrations' watched branches through + `mesh-catalog`'s `registrations` and `registration-changed`, and a build seat's holder from the seat list; the + manifests, the packages, each package's name in its ecosystem (the Go module path, the npm name, which the catalog + graph entry's packages carry) and a run's candidate from the catalog half of its revision view, never the indexer's + state. With no holder of a build seat, a build ends `could not run`, naming the seat, and is asked again on the seat + list's change. Why: ADR 0596 §2. How it is checked: the builder's account holds no grant to the indexer's state. +- §349 **`package-published` is heard by the indexer.** The indexer keeps its package state from `package-published` for + every package, and from the builder's `version.numbered` only for images, bundles and archives. Why: the + publication is the seat's event (ADR 0666 §15). How it is checked: whole (indexer), a published package's version is + in its state. +- §350 **A superseded build's drop is a kept call.** The runner ends a superseded build's step `superseded` at once, and + asks the build seat's `drop` as a kept call (ADR 0668 §1, §2), so it is asked again through every transport fault + and said `call-unanswered` after five minutes in the runner's findings. How it is checked: whole (runner), a drop + unanswered is asked again, and raises `call-unanswered` after five minutes. +- §351 **One rule for asking again.** mesh-sdk's kept-call form (ADR 0668 §7) asks an ask again only while it is open and + not replaced, and every asker and every hand ask-again tool uses it. How it is checked: unit (mesh-sdk), a replaced + ask is not asked again. +- §352 **The assigner's newer-revision check reads its own state.** The want gains no field (*Composing waits per + node*): the assigner keeps in its old `state` section `written` one key per module and watched branch, the source + revision and end count it last wrote that module's wants from, compares a new `pipeline-ended` with it by + compare-and-set, and deletes the key when `registration-changed` says the branch is no longer watched; its tool + `assigner_written` reads it. How it is checked: whole (assigner), an older revision ending after a newer one writes + nothing, and a later end of the same revision writes. +- §353 **The assigner follows the registrations.** `mesh-delivery-contracts`' client `meshcatalogclient` carries a typed + follower of `registrations`, a first view then `registration-changed`, built on `go/views`; the assigner and the + forge module's merge switch on its first registrations (ADR 0667 §10). How it is checked: whole (assigner), it keeps + the trunk-only want until the first `registration-changed`. +- §354 **The assigner's composition step runs the composer in the sandbox.** It asks the seat `node-build-go` the ask `run` + (*What ADR 0656 leaves to this design*), its step keys under `assigner.`, and serves the ask's input by its seat + `mesh-wants`' verb `input`, as the checker's seat does today; the indexer does the same for its validation by + `mesh-catalog`'s `input`. Both are foundation parts, so each keeps its inputs in an old `state` section `inputs`, one + key per input named by its write time and its sha256, deleted when the build seat's `step-ended` of that ask is heard + or a day after its write, whichever is first, the day judged at each new ask over at most 20 of the oldest keys; + `assigner_inputs` and `indexer_inputs` read them. Both verbs' callers are the holders of the build seats. The Go build module reads no revision verb. Why: the + composition step runs on parts built from the heads, in the sandbox (ADR 0666 §17). How it is checked: whole + (assigner), a composition step's `run` is answered and its input read by digest. +- §355 **The assigner says an unanswered `manifest-reads`.** The assigner, the one caller of the node-engine's + `manifest-reads` from G, writes `call-unanswered` into its own findings state, and the condition keeper's reads gain + it in the same change (ADR 0668 §3). The assigner and the runner each declare that state, `findings`, in their old + `state` section, both being foundation parts. How it is checked: whole (assigner, runner, condition keeper), a node-engine + that never answers raises `call-unanswered` after five minutes, and the keeper raises it. +- §356 **Two acting parts in different steps make no cycle.** The assigner's cycle check counts waits within one step only + (*A wait holds only within its step*). How it is checked: the unit rows there. +- §357 **Which provider a requirement points to.** In the assignment graph, by design 27's order: the named seat's + holder, a pin, the provider on the consumer's node, the only provider; otherwise the entry is held, naming the + candidates, and nothing is guessed. A contribution edge runs from a consumer's entry to the provider its requirement + resolves to, wherever that requirement carries values the consumer contributes. How it is checked: unit + (assignment graph), each step of the order and a hold. +- §358 **The catalog's shared view takes a branch.** The client's view of the catalog half is per watched branch, named by + its caller. How it is checked: unit (client), two branches give two views. +- §359 **`difference` names its revisions.** `difference` with none of `from`, `to` and `group` is refused, naming the + missing argument. How it is checked: a unit row. +- §360 **An approved ask binds the placement.** What an approved ask binds to includes the placement's watched branch and + its `build`, beside the fingerprint. How it is checked: whole (assigner), a placement changed after the Approve asks + again. +- §361 **A placement's `build` is a held put.** A placement's `build` is written as a held put of that build, released when + the placement drops it; a put a person wrote is never released by a placement change. How it is checked: whole + (assigner), a placement's `build` holds that build on its nodes, removing it releases the put, and a person's put + stays through a placement change. +- §362 **The indexer's first-start catch-up runs apart from its source handler.** At its first start the indexer takes in + the heads the gate's source names by its catch-up path before it starts its handler of `source-moved`. How it is + checked: whole (indexer), a `source-moved` said during the catch-up is applied after it, once. +- §363 **A stale ask is dropped, backups included.** The assigner drops an ask it raised once the want it asked for is + replaced, a backup ask among them (built in mesh-delivery #200). Why: ADR 0596 and ADR 0668 §2. How it is checked: + whole (assigner), a backup ask whose want was replaced is dropped and not asked again. + +### The condition keeper and the node-engine + +- §364 **The node-engine claims its seat.** The node-engine's manifest claims the node seat `node-engine` (ADR 0563), and + the indexer and the node-engine's own guard read its holder from the seat list, keeping no module name. How it is + checked: lint finds no module name in either. +- §365 **The condition keeper reads its sources from the catalog.** The catalog graph entry carries each kept data a + manifest declares (its name and its section or template); the keeper takes a first view of the catalog half and + reads a module's `findings` wherever its entry declares a state `findings`, and the node-engine's holder from the + seat list. It joins the readers of *Every other reader of which modules exist*, reading every watched branch's newest catalog + revision, so a module watched on any branch is a source. A condition of a module that is in no branch's revision is + cleared, said with the reason `module left the catalog`. Before its first view the keeper keeps raising from the + sources it held, and says `view-waits` after five minutes (*A part with no view says it waits*). Why: no fixed names (ADR 0666 §5), + and the keeper reads findings (ADR 0564 §2). How it is checked: whole (keeper), a fixture module declaring `findings` + is read with no code change. + +### From round 1 of the review by architectural domain (2026-11-10) + +- §366 **The runner's stores say their fill.** *A store filling is said before it is full* holds for the runner's + `pipelines`, `step-outputs` and `kept`: it says their fill in `show` and `kept`, `filling` past 80 % and `room` below + 70 % on its seat, and `full` at the cap, when it starts no new pipeline, says each start it refused, and takes them + up on `room`; nothing is dropped to make room. Why: the runner joins the stores of *Two graphs, one source of truth* + (ADR 0666 §24, ADR 0564 §2). How it is checked: whole (runner), a full `pipelines` refuses a start, said, and + `room` starts it. +- §367 **A forge bridge message is read again on a transport fault.** The indexer reads a message of the forge + bridge's stream again when reading it met a transport fault, as main's `UnreadAgain` does (ADR 0668 §1, §4): the + message is left unacknowledged and its read asked again on the bridge's next answer, while a message the bridge + itself answered as lost stays lost, said in the indexer's journal and in `indexer_pending`. G's own change for it + (`LostAgain`, commit 106fc16b) is not built. Why: one rule for kept calls (ADR 0668), already on main. How it is + checked: the indexer's take-in table, a transport fault reads the message again and a lost answer does not. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index b1c606d5..47810cf7 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -68,2034 +68,16 @@ decisions: This is milestone G's part of [design 47](../47-delivery-from-commit-to-delivered.md): the pipelines per module of research 086 and the two graphs of research 085, one milestone (ADR 0667 §1). The main file keeps the purpose, the -whole shape, the acceptance tests and the milestones; every line milestone G builds is here, moved whole from the main +whole shape, the acceptance tests and the milestones; every line milestone G builds is in this part's files, moved whole from the main file on 2026-11-10, and is cited as "design 47 G §n". A sentence that holds a delivered line and G's change together stayed in the main file, and G builds what it says from G's delivery. A section named in italics is in this part, or, when it is not here, in the main file. -## Pipelines per module - -*ADR 0666, ADR 0667; ADR 0595, ADR 0596, ADR 0448 §2, §4, ADR 0548 §5, ADR 0564 §2, §4, ADR 0283, ADR 0656 §4, §6, ADR 0633 §5, -ADR 0664 §4, ADR 0398 §2, ADR 0593 §9; research 086, Direction.* - -§1 From milestone G's delivery. A change of any module, the delivery system's own parts included, goes from a commit to -the machines through that module's **pipeline**: the pipeline steps its **policy** names, run for each commit or group -on each of the module's **watched branches** by the seat `mesh-pipeline-runner`, ordered by the catalog graph's edges -between artifacts, with no hand act (ADR 0666 §1). Milestone G is the one milestone that builds it, beside the two -graphs, the revision view and the delivery record (ADR 0667 §1). Until G's delivery the sections this one replaces -(*Checks*, *A try*, *The Approve and the merge*, the state table and the trunk-only want) hold as written, and they -check and deliver milestone G itself (ADR 0667 §2); each of them says so in a line of its own. - -``` - a session ──merge-pull (seat git)──► forge ──commit on a watched branch──► forge bridge ──► indexer (mesh-catalog) - │ registrations; a catalog graph - │ per watched branch; the revision - │ validated before take-in - ▼ take-in.taken - pipeline runner (mesh-pipeline-runner) ── asks each pipeline step on the serving seat's work queue, hears step-ended: - │ lint, unit tests, integration tests ──► the build seats (node-build-go, node-build-node, node-build-image) - │ composition ──────────────────────────► the assigner (mesh-wants) - │ code review ──────────────────────────► the runner's own verb `review`, from the reviewer's bus account - │ approval ─────────────────────────────► operator-channel: accepted kind ask, then decided - ▼ pipeline-ended (every step's result), keyed by source revision - assigner ──► wants from a revision whose required steps passed, on the watched branch a placement chose - gate ──► the delivery record keeps the step and pipeline events -``` - -### A module's registration - -- §2 **What it holds.** The seat `mesh-catalog` keeps each module's **registration**: its git origin, the path of its - manifest, its watched branches and its policy, on the bus, every version with who changed it and the warrant of the - operator's Approve (ADR 0666 §2). The indexer is a foundation part, so the registrations are kept in its old sections - (ADR 0619 §1 to §3, ADR 0666 §4): one key per module in its old `state` section `registrations`, and every version on - its old `streams` section's stream `registration-history`, kept with no age limit. - The catalog graph is derived from the registrations and the manifests they point to; the mesh knows modules, and a - repository only as a module's git origin. -- §3 **Changing one is a trusted act.** The seat's verbs `register` (a module whole), `policy` (one module's step - statuses, per artifact), `watch` and `unwatch` (add or remove a watched branch) each raise an authorising ask of tier - approve through the seat `operator-channel` (design 46 §10), naming every value the change moves, one line each - ("`mesh-grants:program` unit tests: `required` → `bypass`"), and write the version only on its warrant; a Decline or - an expiry writes nothing (ADR 0666 §2). Until a record binds each forge account to its bus identity, `register` and - `policy` refuse code review as a `required` step, naming why, so no review gates while its author cannot be told - (ADR 0666 §22; issue 1966, note 157). A manifest moved to another path is the one manifest change that changes the - registration; any other manifest change is a commit, judged at its revision. -- §4 **Said and read.** Each written change is said as the seat's event `registration-changed` (the module, the version, - what moved); the verb `registrations` answers every registration, or one with its history. The indexer derives the - modules it keeps from them, and the forge bridge its watched branches, each by a first view of `registrations` and - then that event, never by reading the ledger (ADR 0666 §3, ADR 0596). -- §5 **An unprotected branch is refused.** `register` and `watch` read a branch's protection through the seat `git`'s read - verb `branch` (its head, and whether a rule refuses a direct push and admits only merges), and refuse a watched branch - the forge does not protect, naming it (ADR 0666 §3). -- §6 **The foundation parts are the manifest validator's list.** The manifest validator's list (ADR 0619 §3) holds - exactly the foundation parts, ADR 0619 §1's and the runner, which joins them (ADR 0666 §4); the checker leaves the - list when it is removed (*How milestone G itself is checked and delivered*). A registration holds no foundation - value. How it is checked: a unit test of the manifest validator refuses a template in the manifest of every part on - its list, the runner's included, and judges every other part's manifest as any part's. -- §7 **No part fixes a name.** Every branch, repository and module name a part needs is read from the registrations and the - catalog graph; the lint step refuses such a name in a string, a constant or a table of code logic, a Go import path - not being code logic (ADR 0666 §5). About 39 such names in 9 parts were found on 2026-11-09 (research 086, Findings); - milestone G removes them. -- §8 **The first registrations are one ask.** At its first start on G's build the indexer, holding no registration, - proposes one registration for every module its trunk revision holds, from what it took in (the origin and path of - each), its watched branch the repository's default branch read through the seat `git`, and the statuses the check - judged until then: lint, unit tests, integration tests and composition `required`, code review `optional`, approval - `bypass`, and `bypass` for every step the artifact's build seat's `steps` does not answer at the seed, so no module - is held by a `required` step it has nothing for (the design's choice under ADR 0666 §7, 2026-11-10); it raises them as one ask naming every registration, as the seat `mesh-module-assigner`'s `adopt` proposes - placement whole (ADR 0567). Until that Approve the runner starts no delivering pipeline and says why, so the - operator is told of this one ask before G's delivery starts. A Decline or an expiry leaves the seed unwritten and - says so; the indexer's tool `indexer_seed_again` raises the ask again, recorded in the hand-act log. -- §9 **Kept for the life of the mesh**, never derived again (research 086, choice 25): the history stream's cap is 64 MiB. - About 120 registrations of about 1 KiB, and an estimated change a week each (a guess, not a measure), grow about 6 MiB - a year, so the cap holds about ten years; past 80 % the indexer says `filling`, as every store of *Two graphs, one - source of truth* does. -- §10 **How it is checked.** Whole (indexer): an unprotected watched branch is refused, naming it; a policy change without - the warrant writes nothing, and a declined one writes nothing; a change from `required` to `bypass` asks, showing - that line; `registration-changed` is said and `registrations` answers the history; the first start proposes the seed - as one ask, and `indexer_seed_again` asks it again after a Decline and after an expiry; a policy making code review - `required` is refused. Unit (the Go build module's lint step): a module name in a string, a constant and a table is - refused, an import path is not. Tests 75, 68 and 83. - -### Watched branches and the catalog graph per branch - -- §11 **Only a watched branch starts a pipeline.** The forge bridge hears every commit of every watched branch the - registrations name, through the seat `git`'s event `branch-moved` (a branch's new head, from the forge's push - webhook, which a merge and an admitted push alike say), and takes the commit from the forge's own record by the seat's - verb `branch`, never from the event's body (ADR 0666 §6). One event for every way a branch moves, because a merge is - not the only one. `branch-moved` is kept on the seat's stream `SEAT_git_branch-moved`, which the bridge hears through - a durable consumer, so a move said while the bridge is down is heard when it starts; at its start it also reads each - watched branch's head the same way. -- §12 **A head with no pipeline is a finding.** A watched branch's head that starts no pipeline within five minutes is the - watchman's finding `pipeline-stands`, naming the branch and its head, raised by the condition keeper (ADR 0564 §2), - unless it waits as progress not owed: a member of a gathered group is governed by ADR 0633 §5's 30-minute bound, and - a merge behind a pending revision of the same branch by its 60-minute bound (ADR 0666 §24). The finding clears on the - runner's `pipeline-started` for that head. -- §13 **The catalog graph per watched branch.** The indexer keeps the catalog graph once per watched branch, from that - branch's manifests at each source revision (ADR 0666 §6): each revision names its branch, and the stores, caps and - pins of *Two graphs, one source of truth* hold per branch. -- §14 **A placement names the branch.** Each module of a placement key names the watched branch its node runs, by default the - registration's first, set by a placement proposal like every other placement change (ADR 0567, ADR 0666 §6). The - assigner computes each node's entries from the catalog graph of the branch its placement names, so a module watched on - `main` and `beta` runs `beta` only where a placement chose it. -- §15 **A source revision per watched branch.** The gate keeps one source revision per watched branch, `source.`, - in place of `source.main` (ADR 0429 §11 to §13 as ADR 0666 §6 changes them), in the old `state` section `source` - already holds, its key's layout being the only change. The gate is no foundation part: the bus module's delivery - waits on no act of the gate's, since the indexer takes a revision in from the forge bridge's stream and the assigner - writes wants from the runner's events, so the gate's other kept data stay in ADR 0595's templates (ADR 0666 §4), and serves it by the seat - `mesh-delivery`'s verb `source` with the branch, saying `source-moved` on each new revision; the indexer and the - node-engine's catch-up read it there, never the gate's state (ADR 0596). Each want names a build-input fingerprint - computed at one such revision. -- §16 **Validated before any take-in, on every branch.** Before it takes a revision in, on every watched branch, the running - indexer asks the manifest validator built from the revision's heads to judge it in the build seat's sandbox (the - ask `run`, *What ADR 0656 leaves to this design*). A revision it refuses stays pending under ADR 0633 §5's - 60-minute bound, its hand acts and its condition `take-in-waits`; the way back is a revert, an ordinary merge - (ADR 0666 §6, §26). A revision on the branch the indexer's own placement chose is taken in only by the indexer it - names (ADR 0633 §1). This validation is no pipeline step and waits on no pipeline, so every delivering pipeline runs - on a revision already taken in. -- §17 **A branch whose protection was removed starts nothing.** On every `branch-moved` the forge bridge reads the - branch's protection again; a move of a watched branch whose protection was removed starts no delivering pipeline, and - the forge bridge writes the finding `branch-unprotected`, naming the branch, which the condition keeper raises and - clears once the protection is back (ADR 0666 §6). -- §18 **A pending revision holds its own branch only.** A merge waits behind a pending revision only on the same watched - branch; other branches move on (ADR 0666 §6, which changes ADR 0633 §3). -- §19 **A refused new indexer holds its branch until a revert lands.** A merge of a new indexer whose own validator refuses - its revision has already landed when it is refused, since the validation runs before take-in and after the merge - (ADR 0666 §6). It stays pending, and every later merge on that watched branch waits behind it, said by - `take-in-waits` at the 60-minute bound, until a revert, an ordinary merge through its pipeline (ADR 0666 §26), lands - and is taken in; nothing takes a later merge in past it. This is the cost of validating every revision with the - validator built from its own heads, the design's reading of ADR 0666 §6, and the operator is told it with the - condition, which names the pending revision and the revert as the way on. -- §20 **How it is checked.** Whole (forge bridge): a commit of a branch no registration names starts nothing; a commit on a - watched branch is taken from the seat's `branch` read, not the event; a head missed while the bridge was stopped is - read at its start. Whole (indexer, assigner): a commit on `beta` gives a revision of the `beta` graph only and moves - only the want of the node whose placement chose `beta`; a revision whose own validator refuses it stays pending and - holds `beta` while `main` moves on. Whole (watchman): a head with no pipeline for five minutes raises - `pipeline-stands`, cleared by `pipeline-started` for that head, and a gathered member does not; a move said while the - bridge was stopped is heard from the seat's stream. Tests 64, 68, 76, 78 and 82. - -### Pipeline steps and the policy - -- §21 **The runner.** The seat `mesh-pipeline-runner`, held once for the mesh by the module `mesh-delivery-pipeline`, - runs each pipeline (ADR 0666 §7). It is a foundation part, since the bus module's own delivery waits on its pipeline - (ADR 0666 §4): its kept data are in old sections (ADR 0619 §2), it waits on no bus module process to start, and it - joins the manifest validator's list of foundation parts, which refuses its manifest asking a template (ADR 0666 §4, - which changes ADR 0619 §1 and §3). It reads the policy from the registrations, asks each step that is not `bypass` of - the seat that serves it, and says each step's start and end and the pipeline's end (*Health and going back*, below). -- §22 **The steps and who serves them.** Lint, unit tests and integration tests are the build seats' (`node-build-go`, - `node-build-node`, `node-build-image`): each gains the verb `steps`, which answers which pipeline steps an artifact - can run by its language's convention (as *The check from release 4* says the tests are found), and the accepted kind - `step` on its stream `check`, which runs one. Composition is the assigner's, an accepted kind `step` of the seat - `mesh-wants`. Code review comes in through the runner's own verb `review`. Approval is asked as the seat - `operator-channel`'s accepted kind `ask`, on its work queue by the runner's name (design 46 §10, *Asks, warrants and answers on the bus*; - the seat has no verb `ask`), and heard on its event `decided` (ADR 0666 §7, §10). A step with nothing to do ends at once as - `nothing to run`, never as passed. -- §23 **The policy.** Each step's status, per module and per artifact, is `required` (runs; holds the delivery until it - passes), `optional` (runs; a failure is shown and may be accepted) or `bypass` (nothing runs; said as bypassed), - kept in the registration and never in a manifest: the manifest validator refuses a manifest that names a status - (ADR 0666 §7; research 086, R3). A `required` step that ends `nothing to run` holds the delivery and names the step. -- §24 **Asked on a work queue, ended by an event.** The runner asks each step on the serving seat's work queue and hears - its end as that seat's event `step-ended` (the record's `step ended`), on - `mesh.seat..event.step-ended.`, the node that ran it in the subject and each holder's account granted only - its own node's subject, through a durable consumer of its own, so a step outlives a restart of either side (ADR 0666 - §7, ADR 0363 §2). A build seat's holder restarted mid-step gets the step again from its work queue and finishes it - (ADR 0664 §1). A unit or integration test step says its test counts in `step-ended`, and `pipeline-ended` names any - repository over 50 integration or 500 unit tests, and any catalog module over them (ADR 0666 §8; the test budget). A step's outcome is one of `passed`, `failed`, `nothing to run`, - `bypassed`, `carried` (with the pipeline it was carried from), `could not run` (naming the part not heard), - `waiting on `, `accepted` (a failed `optional` step a person accepted, with who and why), `superseded` and - `cancelled`. -- §25 **Carried, not run.** A step whose build-input fingerprint and policy did not change is carried by its package - digests, as ADR 0548 §5 holds, the runner making the carry instead of the checker: a test step runs only the packages - with no kept result for their digest, and a run on request's results are reused when the merged commit's inputs match - (ADR 0666 §8; research 086, choice 24). An `approval` step is never carried. -- §26 **Approval comes last.** The runner asks the `approval` step only once every other `required` step of the pipeline - passed, so the operator approves a commit the mesh already judged, the one place ADR 0661 §3 had the check gate the - ask. A failure after an approval is said, as a failed `optional` step is, and the approval stays. -- §27 **Kept on the bus, in old sections.** The runner keeps each pipeline's state in its old `state` section `pipelines`, - one key per pipeline keyed by its watched branch, source revision and module, written by compare-and-set so a holder - replaced mid-pipeline and its successor never both write one; its step log on its old `streams` section's stream - `step-log`; each step's output in its old `objects` section `step-outputs`, named in the log; and each package's - passing result per level in its old `state` section `kept`, under the digest of what its tests read (ADR 0666 §4, §8; - *The runner's kept data*, below). `kept` starts empty: the runner takes no verdict of the checker's, since a step's - result is taken only from the bus account of that step's seat holder (ADR 0666 §21), so the first pipelines after G - run each package's tests once and carry from then on (the design's choice, 2026-11-10). -- §28 **How it is checked.** Whole (runner): a step with unchanged fingerprint and policy is carried and says from where, - and an `approval` step is not; a module with no integration tests shows `nothing to run`, and a `required` unit-test - step on a module with no tests holds its delivery; a bypassed step is said; a runner restarted mid-step hears the - step's end and finishes the pipeline; two runners writing one pipeline key, one loses by compare-and-set; a test step's - counts are in `step-ended` and a repository over budget is named in `pipeline-ended`. Whole (Go build module): `steps` - answers the steps of a fixture artifact, a `step` ask ends with `step-ended` on its own node's subject, one on another - node's subject is refused by the bus, and a build module restarted mid-step finishes it. Unit (manifest validator): a manifest naming a status is refused. Tests 65, 66, 73 - and 88. - -### What holds a delivery - -- §29 **The assigner writes a want only from a passed pipeline.** The runner says each pipeline's end as `pipeline-ended` - (the record's `pipeline ended`), keyed by source revision, with every step's result. The assigner consumes it, and - writes a want only from a source revision whose every `required` step passed for the modules it moves, on the watched - branch a placement chose, by compare-and-set on a newer revision, so an older revision ending after a newer one - writes nothing (ADR 0666 §9). The compare-and-set is keyed per module and watched branch: it compares a revision only - with the last one written for the same module on the same watched branch, so a revision of `beta` ending after a - newer one of `main` writes its own want and refuses neither (the design's choice under ADR 0666 §9: source - revisions are kept per watched branch, `source.`, and two branches' revisions have no order between them). This is one more rule beside the assigner's others (ADR 0429 §6), checked per module. - From G's delivery it replaces the trunk-only want (ADR 0656 §2 as ADR 0666 §9 changes it). For a group's source - revision the runner says one `group-ended` once every member's modules have ended, and the assigner writes the - group's wants only on it: a member's own `pipeline-ended` writes nothing (ADR 0666 §9). -- §30 **A failed `optional` step holds nothing.** It is shown in `pipeline-ended`, in the delivery record and in the runner's - `show`; a person may accept it by the runner's verb `accept`, recorded in the hand-act log with who and why. -- §31 **A newer commit supersedes an older pipeline of a module whose fingerprint it changed.** On a newer commit of the - same watched branch the runner ends a module's older running pipeline as `superseded` only when the newer commit - changed that module's build-input fingerprint (ADR 0666 §9); a module the commit leaves as it was finishes its - pipeline. A newer publish of a package likewise supersedes the rebuild pipelines an older publish of it started. A - superseded pipeline's waiting asks are dropped by each build seat's `drop` (ADR 0664 §4), naming - the newer commit as the cause, so a cancelled pipeline leaves no ask on a stream (a gap G's prepare found: a cancelled - build withdrew only the builder's wait). -- §32 **A step nothing starts ends its pipeline.** A step whose prerequisite nothing starts (a seat with no holder, a - prerequisite step `bypass`) ends the pipeline refused within five minutes, naming the step and what it waits for, - never as a silent wait (research 086, R5); a step whose seat has no holder ends its pipeline failed, naming the seat - (ADR 0666 §17). -- §33 **A failed head is said to its author, then to the operator.** A `required` step that failed, or ended `could not - run`, on a watched branch's head is the runner's finding `pipeline-failed`, naming the module, the branch, the commit - and the step; the condition keeper raises it as the commit's author's for two hours and then the operator's, as ADR - 0505 §5 and §6 have a delivery whose cure is its author's new head, and clears it when a newer revision of that branch - passes that step (ADR 0666 §24). The author is the forge's record of the commit, read through the seat `git`. -- §34 **The delivery record keeps every step.** The gate's delivery record consumes the step and pipeline events and keeps - them as long as the record (ADR 0666 §9; *The delivery record*). -- §35 **How it is checked.** Whole (assigner): a failed `required` step writes no want; a failed `optional` step holds - nothing; an older revision's `pipeline-ended` after a newer one's writes nothing, and a revision of one watched branch - ending after a newer one of another writes its own want; a group with one member passed and - another still running writes no want until `group-ended`. Whole (runner): a newer commit that changed a module's - fingerprint supersedes its older pipeline and drops its asks, and one that left it unchanged lets it finish; a step - with no holder ends the pipeline naming the seat within five minutes; a failed `required` step on a head raises - `pipeline-failed`, the author's for two hours and then the operator's, cleared by a newer passing revision. Tests 66, - 67, 87, 89 and 98. - -### Approval is a step, and a session merges a pull request - -- §36 **No Approve per merge.** The operator's approval is the pipeline step `approval`, its status in each module's policy; - no Approve is asked per merge, and the delivery gate's merge turns and its Approve per merge go (ADR 0666 §10, which - changes ADR 0661 §3 to §5). The operator is asked only where a module's policy says `approval`, and for registrations - and policy changes. -- §37 **The approval is an ask of the runner's.** The runner raises an authorising ask of tier approve as the seat - `operator-channel`'s accepted kind `ask`, under its own name, its `about` the module and the watched branch only, so a newer commit's ask - replaces the older one's (design 46 §10). The warrant ends the step passed; an ask that is declined, expires or is - refused ends it `declined`, and one that is replaced ends it `superseded`. Only a declined `approval` step is asked - again, by the runner's verb `ask-again` (ADR 0666 §10). While it waits the runner - says `wait-held` waiting on the operator, never a stall (ADR 0283). -- §38 **A session merges a pull request.** Any session the tool runner proves, and the operator, merges by calling the seat - `git`'s verb `merge-pull` over the mesh, which merges with the forge tools' account (ADR 0398 §2) at the head the - caller names. It judges that this is still the pull request's head and that the branch's protection refuses a direct - push, the protection read again on every `branch-moved` of the branch, and it judges the head's runs by its modules' - policies: it refuses a head when any run at its build-input fingerprint failed a step a policy marks `required`, and - merges a head whose `required` steps passed or that has no run yet, a failed `optional` or a `bypass` step refusing - nothing; it never waits for a run and never asks an Approve (ADR 0666 §11). The forge keeps only its branch protection. -- §39 **Judged from its own state, never by a call.** The holder of the seat `git` consumes the runner's `step-ended` - through a durable consumer of its own and keeps, in its own state `failed-steps`, each failed `required` step by - module and build-input fingerprint, with the commits it was seen at and the run that failed it, until none of those - commits is a watched branch's head or the head an open pull request names (*The runner's kept data*); so a head - merges while the runner is down, judged on what it already heard (ADR 0666 §11, ADR 0596). A head's fingerprints are - those its runs named for its commit; a step that failed at that fingerprint in any run, a group's or another commit's - of the same content included, refuses it, unless a later run of that same step at that fingerprint, asked again by - the operator or the healers (the runner's `rerun`), passed: that pass clears the failure from `failed-steps` (ADR 0666 - §11). The runner's `step-ended` says that the step was a rerun and the caller the runtime proved, and the forge module - clears a failure only for a rerun whose caller is the operator or a healer; a plain pass, or a rerun asked by anyone - else, clears nothing. A run still running is judged by the steps it has ended, since the merge never waits. A refusal names the run, the module and the step, so the session knows what to fix. The gate merges nothing, and its forge merge token is - removed with the checker's buckets and bus account, after the first pipeline after G's delivery has delivered a change - end to end and on the operator's word (ADR 0667 §12; *How milestone G itself is checked and delivered*); the forge bridge hears the landed commit as it hears every commit of a watched - branch. -- §40 **The operator-channel hides no ask.** It keeps design 46's three open asks per asker, never holds the newest back, - and replaces an open ask by the same asker's ask with the same `about` (ADR 0666 §12; design 46 §6). -- §41 **How it is checked.** Whole (forge module, forge bridge): a session's `merge-pull` at its head merges with no run yet, - and with a passed one, and asks no Approve; with the runner stopped a head merges; a head at whose fingerprint any - run failed a `required` step, a head that moved and a branch whose protection was removed since its last move are - refused, each naming why; a failed `optional` step refuses nothing. Whole (runner): an `approval` step is asked last, - a Decline and an expiry each end it `declined` and `ask-again` asks it once more, and a newer commit's ask replaces the - older and ends its step `superseded`. - Live: a declined approval and a fourth ask each reach the operator. Tests 71 and 91. - -### Groups - -- §42 **Formed from the branch name.** The gate forms a group from a shared branch name carrying its issue number, as it - does today without the fixed list of trunk names (ADR 0666 §13). -- §43 **A member reaches its group by landing.** A member has reached its group when its commit lands on its modules' - watched branch. The forge bridge sets the group on each landed member's commit, reading the members from the seat - `mesh-delivery`'s verb `groups`, and publishes `forge.group.merged` when that verb says every member has (ADR 0666 §13, - which changes ADR 0633 §4). -- §44 **One source revision, one pipeline per branch.** The indexer keeps landed members pending until the group's end and - takes them in together as one source revision of that watched branch, saying `take-in.taken`; the runner starts the - group's one pipeline per watched branch on that event, and a member of a gathered group starts no pipeline of its own - (ADR 0666 §13). - When one member's `required` step fails, none of the group's modules is delivered (research 086, R5). -- §45 **A group that never completes is said.** By ADR 0633 §5's 30-minute bound and its condition; `bridge_group_end_again` - says a group's end again from the gate's group verb, never the gate's `merges` state (ADR 0666 §13, which changes ADR - 0633 §5). -- §46 **How it is checked.** Whole (indexer, gate, forge bridge): a group of three repositories, its own packages included, - is one source revision and one pipeline; one member's failed `required` step delivers none; a group with a member that - never lands raises `take-in-waits` at 30 minutes. Tests 67 and 81. - -### Dependencies between artifacts - -- §47 **Six kinds of edge between artifact references.** The catalog graph's edges run between **artifact references**, - `:`, such as `mesh-grants:program`, of six kinds (ADR 0666 §14): - - | Kind | B before A | Derived from | - |---|---|---| - | 1 | B published before A builds | a package A's build source imports (ADR 0573 §3) | - | 2 | B published before A delivers | an artifact A's manifest fetches by reference at its start | - | 3 | B delivered before A builds | a build module A is built by, and the toolchain its agreed version names (ADR 0584 §1) | - | 4 | B delivered before A delivers | a provision A requires and B provides, bus state among them (ADR 0614 §5), and a format's reader (below) | - | 5 | B has acted for A's new version | a seat's acting rule (*What ADR 0656 leaves to this design*): a grant, a credential, a verdict, bus state | - | 6 | B runs a version that serves A | a verb or a manifest word A uses and B serves or reads (ADR 0614 §2, §4), the build seats' `kept-artifact` among them | - - Each kind is derived from what manifests and seat definitions already say, and declared per artifact in the manifest's - artifact entry, its field `needs`, only where nothing else says it. Today's edges run between modules, and only a - package edge names an artifact; at module level the tool runner and the grants part looked like a cycle, per artifact - they are a chain (`mesh-grants:program`, then `node-tools:runtime`, then `mesh-grants:tools`). -- §48 **Removal runs the other way.** When something goes away the order is reversed: a seat moved to a new holder has its - old holder released before the new one claims (ADR 0666 §14). -- §49 **The edges alone order.** The build and the delivery order by these edges alone; a cycle of delivery edges (kinds 2, - 4, 5, 6) is delivered as one step, and a cycle of build edges (kinds 1 and 3) is refused, naming every artifact on it - (ADR 0666 §14; research 086, R17). The cycle rule is the indexer's, in the catalog graph it keeps, so the checking - program's cycle rule goes with the checker. -- §50 **A format's reader first.** A format a running reader cannot read is preceded by its reader, an edge of kind 4 on - every watched branch (ADR 0666 §14): the indexer's manifest validator for a new field of a seat definition or a - manifest, and on each node the node-engine that reads a manifest's new fields before any manifest that writes them - (ADR 0667 §4). -- §51 **How it is checked.** Unit (indexer's graph): a chain per artifact where modules would cycle; two programs needing - each other's new verb are one step; two artifacts each needing the other's new package to build are refused, naming - both; a seat moved to a new holder releases the old first. Live: the tool runner and the grants part delivered in - the order `mesh-grants:program`, `node-tools:runtime`, `mesh-grants:tools`. Test 79. - -### A publish rebuilds - -- §52 **Numbered at publication.** The builder numbers a package's version when it is published, never only when a build - is claimed, and says the publication as the event `package-published` of its seat `mesh-builds` (ADR 0666 §15; design - 51, *The versions*). The seat is new, held once for the mesh, defined in `mesh-delivery-contracts`; the builder held - none until now, and its waits stay on its own stream in this milestone. -- §53 **Every dependent is rebuilt against it.** On `package-published` every artifact with a kind-1 edge to the package is - rebuilt against the new version, and the runner, which consumes the event, starts its pipeline; nothing else is - rebuilt, and a change to tests alone publishes nothing (ADR 0666 §15; research 086, R18). -- §54 **A second start writes nothing.** The runner starts a pipeline on two events, the indexer's `take-in.taken` (ADR - 0666 §13) and `package-published` (ADR 0666 §15). When both reach it for the same source revision and module, the - second start writes nothing: the runner writes a pipeline's key (watched branch, source revision and module, *The - runner's kept data*) by compare-and-set on its absence, so one revision and module has one pipeline. The design's - choice under ADR 0666 §13 and §15: the key the runner already keeps is the one place a start is judged, so neither - event's producer needs to know of the other. -- §55 **Unread packages refuse.** A build that cannot read the published packages refuses and names the unread state - (ADR 0666 §15). -- §56 **How it is checked.** Whole (builder, indexer): a mesh-sdk merge changing one package publishes one version, and only - the artifacts importing that package rebuild; a merge changing only tests rebuilds nothing; a build whose packages - cannot be read refuses naming the state. Whole (runner): a `take-in.taken` and a `package-published` for the same - source revision and module start one pipeline, the second writing nothing. Tests 70, 80 and 101. - -### Any size - -- §57 **A value over the limit is an object.** mesh-sdk's own small object store package (ADR 0595 §9) puts a value over - the bus's per-value limit in the object store beside its state and keeps the object's name in the key; a verb's reply or an event over the - per-message limit carries the object's name (ADR 0666 §16). A replace writes the new object and sets the key by - compare-and-set; the object it replaced stays, as one whose key was deleted does, listed as `key gone` since no key - names it, and only the drop tool removes either, on a person's word: the mesh removes kept data only through `drop` - and `recreate`, each on a person's word (ADR 0595 §4; ADR 0666 §16). -- §58 **One object store per state, granted to its owner.** Each state asked by the template `state` gets its object store - beside it, `-objects`, a template form of ADR 0595 that the bus module provides and the grants part grants to - the state's owner alone (ADR 0666 §16); a foundation part's large values go in an old `objects` section of its own. - Its size is the template's (ADR 0595 §3). An object store holds no space until a value is written to it, so the many - it adds reserve nothing; the few values over the limit (a delivery record, a catalog graph revision list, a test's - output) are estimated, as a guess, at under 1 GiB across the mesh. -- §59 **An object sent is readable by the modules it is for.** A reply's or an event's object is written to the sender's - **sent store**, `_sent-objects`: the bus module plans it, with no manifest field, for every module of the new - form that serves a verb or emits an event, and a foundation part declares it as an old `objects` section - `sent-objects` (ADR 0619 §2). Its cap is the objects template's (ADR 0595 §3), and it keeps an object 7 days, after - which the bus expires it. That age is transport, not kept data: an object sent exists only to carry one reply or one - event, read within its call's bound or its consumer's lag, so its expiry is not a removal of kept data that ADR 0595 - §4 holds to a person's word (the design's choice; 7 days is a guess at the longest a consumer lags, read from the - fill tools once G runs). A full sent store is the bus module's `full` and its condition (ADR 0595 §6), and the sender - then refuses the large reply or event loudly, naming the store. The grants part derives from the manifests a standing grant to read them: the - callers its seat definition names for the verbs it serves, and the consumers whose `consumes` declare its events; - no other module may read them (ADR 0666 §16). The grant follows the manifests, so it is written when they change, - never per object. A consumer's reader of - the form is delivered before its producer writes it, an edge of kind 4. -- §60 **Its tools come with the package.** The package serves each module that imports it `_state_objects` (the - objects of each state, with their size, the key that names each or `key gone`, and the fill against its cap), - `_state_objects_drop` (drop the objects whose key is gone, on a person's word as ADR 0595 §4 removes kept - data), `_sent_objects` (each sent object, its size, when it was sent and to which verb or event, its age - against the store's and the fill against its cap) and `_sent_objects_drop` (drop sent objects older than an - age a person names); each drop names who asked and is recorded in the hand-act log. -- §61 **Its cost.** Only the modules that import the package are built with it, so no other module is rebuilt for it: a - package published before its users build (ADR 0573 §2, ADR 0667 §3). -- §62 **How it is checked.** Whole (mesh-sdk, against the real bus): values of one, ten and a hundred times the per-value - limit stored, sent as a reply and as an event, and read back whole by a caller and a consumer the manifests declare, - and refused to a module that declares neither; a replaced large value leaves its old object listed `key gone` beside - the new one, and a key deleted leaves its object listed `key gone`, each removed by the drop tool and by nothing else; - a sent object is readable within its 7 - days and expired after; each drop without who asked is refused. Tests 69 and 90. - -### The pipeline makes the check, and the checker goes - -- §63 **Every step runs on parts built from the change.** Every pipeline step that runs a change's code runs on parts built - from the change's own heads, in the build seat's sandbox, with no network and no bus account (ADR 0666 §17, ADR 0448 - §2, §4): the composition step composes each machine's declaration with the composer built from the heads and refuses - a change in which two modules own one path; the build seats' steps run lint and tests. A step whose serving part the - change alters runs from the change's build of it, built within 20 minutes or ending `waiting for a build node` or - `did not build`, the first tier of *The check on the change's own acting parts* made the runner's. -- §64 **The run on request.** The runner's verb `run` takes one or more members, each a module at a named commit, runs them - through their pipelines and delivers nothing, replacing the checker's try (ADR 0666 §18). Its callers are the - sessions, as the agents' bus identities the tool runner proves, and the operator, and nothing else, since a run of a - group builds and publishes its packages' tagged versions (design 51). The runner says `run-started`, the indexer builds - the run's candidate from it, and the candidate's composition step writes no want (ADR 0656 §9); the candidate is - dropped on that run's `pipeline-ended`. A step that could not run ends `could not run`, never as passed. A - composition step for a node that sleeps passes it with a warning (ADR 0593 §9). -- §65 **The runner is an acting part in the checker's place.** The acting rule's step `check` becomes the runner's step - `pipeline` (*What ADR 0656 leaves to this design*): its own change runs on its own new version, a failing new runner - holds while the old one runs pipelines (ADR 0656 §4), and the pipelines in flight when its holder is replaced are - taken up again from its kept state (ADR 0666 §19). -- §66 **What the checker did, and where it goes.** - - | The checker's | From G's delivery | - |---|---| - | the merge check, one status `mesh/check` | the pipeline, its result in `pipeline-ended`, the runner's `show` and the delivery record; no status on the forge | - | the try | the run on request | - | the composition check | the composition step, the assigner's | - | the kept verdicts and the carry | the runner's `kept`, which starts empty, and its carry (ADR 0548 §5); the checker's verdicts go with its buckets | - | the checking program: the manifest validator at the heads, the cycle rule, the composer, the rule names | the indexer's validation before take-in; the indexer's cycle rule; the composition step; `widens` in the pipeline's end | - | the `run` ask, published by the checker's account | published by the runner's account and the indexer's (its validation), each with step keys under its own name | - | its buckets and bus account | removed with the gate's forge merge token after the first pipeline after G's delivery has delivered a change end to end, on the operator's one word, the buckets through `drop` after its snapshot, whose restore is tested first (ADR 0667 §12) | - -- §67 **How it is checked.** Whole (runner, assigner): a run on request of `feature/1966-x` delivers nothing, and a call of - `run` from another account is refused; a composition step refuses two modules owning one path; a step whose part - cannot be heard ends `could not run`; a node asleep passes with a warning; a runner stopped mid-step takes its - pipelines up again. Live: once the checker is removed no module claims the seat `mesh-delivery-checker`. Tests 64, - 84 and 88. - -### Who may say what - -- §68 **Callers per verb, in the seat's definition.** Each seat definition names, per verb, the callers allowed to call it, - in its field `callers` (holder roles, modules, the operator, the healers), which the grants part reads as its one - source of caller limits; the runner's verbs are the first (ADR 0666 §20). A verb whose definition names no `callers` - admits only the seat's holder roles and the operator, so a field left out opens nothing. It replaces the holder's - trusted config value `callers` of the seat `git` and the package seats (design 51), which no holder set for most - verbs. A format its running reader cannot read, so its reader is delivered first (ADR 0667 §4). -- §69 **A step's result from its holder only.** The runner takes a step's result only from the bus account of that step's - seat holder, naming the commit and the build-input fingerprint; a result from another account, or naming another - commit or fingerprint, is refused and said (ADR 0666 §21). -- §70 **A review gates, once it can.** A code review's result is taken through the verb `review` from the reviewer's bus - account and refused from the commit's author, so a review gates where a policy says `required` (ADR 0666 §22, which - changes ADR 0659 §2). Until a record binds each forge account to its bus identity no policy may say it (*A module's - registration*; issue 1966, note 157). -- §71 **A widening is said, never refused.** A change that alters any part serving a step (the runner, step detection, the - lint rule, a build seat's test runner, the composer), or widens an acting rule, a grant or a credential, says so in - its `pipeline-ended`, its field `widens` naming each. The running runner computes it, from the sources of the change - against those of the watched branch, never the runner built from the change, so a change cannot hide its own - widening; nothing refuses it for that (ADR 0666 §23, the operator, 2026-11-10). The teller passes `widens` on to every - channel, as it does `recovery-merged`. This is what replaces ADR 0656 §8's naming in the Approve ask. -- §72 **How it is checked.** Whole (runner, grants part): a forged step result and an author's own review are refused; a - caller not in a verb's `callers`, and any caller but the holder roles and the operator of a verb with no `callers`, - are refused by the bus; a change widening a grant, and one altering the Go build module's test runner, each name it in - `widens`, computed by the running runner, and are delivered. Tests 85 and 86. - -### Health and going back - -- §73 **The runner's events.** Each on `mesh.seat.mesh-pipeline-runner.event....`, - a branch or group name escaped as a key's name is, declared in the seat's definition and published by the runner's - account alone (ADR 0666 §24, ADR 0363 §2): - - | Event | Says | - |---|---| - | `pipeline-started`, `pipeline-ended` | a pipeline's start, and its end with every step's result, its test counts, the source revision and `widens` | - | `group-ended` | a group's one pipeline's end, on which alone its wants are written | - | `run-started` | a run on request's start, with its members at their commits | - | `step-started`, `step-ended` | each step's start and end: the step, its status in the policy, its outcome, its build-input fingerprint, its test counts, for a carried result what it was carried from, and for a rerun that it is one and the caller the runtime proved; a bypassed step too | - | `wait-held`, `wait-ended` | a step's wait: on a person (approval, code review), on a build node, on the revision view | - -- §74 **What a session hears after its merge.** Between its `merge-pull` and the delivery, a session waits with `mesh_wait` - on the runner's `pipeline-started` and `pipeline-ended` of its module, branch and commit, and then on the status - view's `delivered` of the source revision; a failed head reaches it as `pipeline-failed` (*What holds a delivery*). -- §75 **Nothing stands still unseen.** The watchman times each step's owed progress from `wait-held` and `step-started`, - never by run length; a step or a watched branch's head standing still five minutes is its finding `pipeline-stands`, - raised by the condition keeper and cleared on the step's end, or for a head on its `pipeline-started` (ADR 0564 §2, - §4). A step waiting on a person is said as waiting on that person (ADR 0283), never as a stall; a group waiting for - its members, and a merge behind a pending revision, are governed by their own bounds (ADR 0666 §24). At its start the - watchman seeds its open waits from the runner's `stands`, as it seeds `take-in-waits` from `indexer_pending`. -- §76 **The runner's own health.** Its manifest declares its health check, run outside its process (ADR 0240): the runner - answers `show`, and its durable consumers of `step-ended`, `take-in.taken`, `package-published` and `decided` are - each less than five minutes behind; a failed check names which. -- §77 **The runner waits for its view.** With no revision view it says so in `_first_views` and raises `view-waits` - after five minutes, as every part does (ADR 0656 §6). -- §78 **Going back is one act.** A failing new version holds while the old keeps running (ADR 0656 §4); a return to an - earlier build is one value in the placement, the module's `build` in its placement key, set by a placement proposal - (ADR 0666 §24). -- §79 **How it is checked.** Whole (runner, watchman): a step whose worker dies raises `pipeline-stands` within five minutes; - a head with no pipeline raises it and its `pipeline-started` clears it; a step waiting on the operator raises nothing; - a runner restarted mid-step takes its pipelines up again; its health check fails naming a consumer behind. Whole - (assigner): a placement's `build` delivers the earlier build. Tests 76, 77 and 88. - -### A recovery fix is an ordinary merge - -- §80 **No recovery merge verb.** A fix that recovers the mesh is merged like any change on a watched branch, by a - session's `merge-pull`, and runs its pipeline; no trusted recovery merge verb is built (ADR 0666 §26). The shape this - design sketched for one (a verb `merge` of the seat `mesh-delivery` with `recovery-callers`) is dropped. -- §81 **Named a recovery, said everywhere.** Its author names it a recovery by the seat `mesh-delivery`'s verb `recovery` - (the commit and why), never by a pull request's text: the gate marks the delivery record `recovery` with the caller - the runtime proves, and says `recovery-merged`, which the teller passes on to every channel (ADR 0666 §26). A widening - it carries is said in its `pipeline-ended` like any other (*Who may say what*). -- §82 **How it is checked.** Whole (gate): a merge named a recovery is marked in its record and `recovery-merged` is said; - a recovery named by a description is not. Test 92. - -### The tools - -§83 Every new state has a tool that reads it and every new act a tool that starts it by hand (ADR 0666 §25), against the -four lines a module's tools answer; every read tool of a store also answers its fill against its cap: - -| Part (seat) | Read everything it holds | Do by hand what it does itself | Recover without help | -|---|---|---|---| -| the runner (`mesh-pipeline-runner`) | `show` (one pipeline whole, its steps, step log and outputs), `list` (the pipelines of a module, a branch or a commit), `stands` (what stands still, since when), `why-carried` (where a step's result was carried from), `kept` (the kept results by digest) | `start` (a delivering pipeline for a watched branch's head), `run` (the run on request), `rerun` (one step again) | `accept` (a failed `optional` step, with why), `cancel` (a pipeline, dropping its asks), `ask-again` (a declined `approval`) | -| the indexer (`mesh-catalog`) | `registrations` (with each one's history); `indexer_edges` (an artifact's edges per watched branch), `indexer_order` (the order of a change, its cycles named), `indexer_catalog_difference` (two revisions); `indexer_pending` (a group with its missing members) | `register`, `policy`, `watch`, `unwatch` (each on the operator's Approve) | `indexer_seed_again` (the first registrations' ask raised again), `indexer_take_in_pending` (a group taken in as gathered), `indexer_drop_pending` (dropped) | -| the gate (`mesh-delivery`) | `source` with a branch (`gate_source`: every branch's source revision) | — | `gate_source_again` (say `source-moved` again for a branch) | -| the forge module (`git`) | `git_failed_steps` (the failed `required` steps it holds, by module and fingerprint, with the run that failed each) | — | `git_failed_steps_drop` (drop one, with why) | -| the grants part (`mesh-grants`) | `grants_callers` (the callers each verb admits, read from the seat definitions) | — | `grants_why_refused` (for an account and a verb, the grant it lacks and the definition's line that leaves it out) | -| the build seats | `steps` (the steps detected for an artifact) | `steps` with `again` (detect again) | `drop` (ADR 0664 §4) | -| the builder (`mesh-builds`) | `builder_versions` (a package's published versions) | `builder_publish_again` | — | -| the assigner (`mesh-wants`) | `assigner_why_not` (why a module is not on a machine yet: the pipeline it waits for, its hold, the placement, the node's last `running`) | — | `assigner_release` | -| the watchman | `watchman_views` (how far behind each part's view is, from each part's `view-waits` and first-view tools) | — | each part's `_first_view_again` | -| the operator-channel | `asks` (with each asker's asks held back) | — | `ask-again` (offer a held-back ask now) | -| every module that imports mesh-sdk's small object store package | `_state_objects`, `_sent_objects` | — | `_state_objects_drop`, `_sent_objects_drop` | - -§84 Every recover tool writes its act, who and why in the hand-act log of the seat `mesh-hand-acts`. How it is checked: -each tool answers its whole-module test's case, every store's read answers its fill, and `mesh_describe` reads each -(test 93). - -### The runner's kept data, and the build capacity - -§85 Each kept form names its section or template, its retention and its cap; a state template fixes its own size (ADR 0595 -§3), up to 64 MiB. The numbers are estimates, to be read from the fill tools once G runs: an estimated 20 merges a day -(as *The delivery record*), each starting a pipeline for about 3 modules of its repository whose fingerprint it changes, -and an estimated 2 mesh-sdk publishes a day, each rebuilding about 120 artifacts and starting about 120 pipelines: about -300 pipelines a day, of about 6 steps each. - -| Part | Kept data | Section or template | Retention | Cap and growth | -|---|---|---|---|---| -| the runner | `pipelines`: one key per pipeline, keyed by watched branch, source revision and module, its steps, statuses, outcomes, fingerprints and test counts | old `state` section | a pipeline a delivery record cites, deleted with that record (90 days); a superseded pipeline, a run on request's and one no record cites, 7 days after its end; always while it is its branch's newest for its module | 256 MiB; about 300 pipelines a day of about 4 KiB is at most about 105 MiB at 90 days, under half the cap | -| the runner | `step-log`: one entry per step's start and end, its output by name | old `streams` section | by its age, 90 days, as long as the delivery records that cite its steps | 128 MiB; about 3,600 entries a day of about 0.2 KiB is about 63 MiB at 90 days | -| the runner | `step-outputs`: a lint or test step's output | old `objects` section | 7 days | 1 GiB; about 1,200 steps run a day (the rest carried) of about 50 KiB is about 420 MiB at 7 days | -| the runner | `kept`: a package's passing result per level, by digest | old `state` section | 30 days unread | 128 MiB; a publish changes the digest of about 1,000 importing packages at two levels, so about 4,000 entries a day of about 0.3 KiB, about 36 MiB at 30 days | -| the indexer | `registrations` and its stream `registration-history` | old `state` and `streams` sections | the life of the mesh, never derived again | 64 MiB (*A module's registration*) | -| the indexer | catalog graph revisions, per watched branch | objects in the old form (ADR 0619 §2) | while a record pins it or it is its branch's current | 512 MiB, as *Two graphs, one source of truth*; a second watched branch on a few modules adds a few MiB | -| the gate | `source`: one key per watched branch | its old `state` section, as today | 64 revisions of each key; a branch's key deleted when `registration-changed` says the last registration watching it unwatched it (`unwatch`) | a few KiB | -| the forge module | `failed-steps`: failed `required` steps by module and fingerprint, with the commits each was seen at | state | an entry deleted once none of its commits is a watched branch's head or the head an open pull request names, judged on each `branch-moved` and each pull request's close | the template's 64 MiB; an estimated 50 failed steps a day of about 0.5 KiB, most gone within days, is under 1 MiB | -| every module that imports the object store package | `-objects` beside each state | objects | an object no key names, its key deleted or its value replaced, until `_state_objects_drop` on a person's word (ADR 0595 §4, ADR 0666 §16) | the template's; nothing reserved until written | -| every sender of the new form, and a foundation part by its old `objects` section | `_sent-objects` (`sent-objects`) | objects, planned by the bus module with no manifest field | 7 days, transport (*Any size*) | the objects template's cap; full is the bus module's `full` | -| the grants part, for each seat event a definition keeps | `SEAT__`, among them `SEAT_mesh-pipeline-runner_*`, `SEAT__step-ended`, `SEAT_git_branch-moved`, `SEAT_mesh-builds_package-published` and `SEAT_mesh-catalog_registration-changed` | a stream the grants part makes with its durable consumers | the seat definition's age (`keep`), 90 days unless the definition names less | the seat definition's cap (`keep`) | - -§86 **The build capacity.** A pipeline runs a module's steps once per commit on a watched branch, carrying what did not -change, where the check ran before the merge and carried after each trunk move; a group runs one pipeline per watched -branch; a second watched branch runs the steps of the modules it carries once more. A mesh-sdk publish asks about 120 -lint steps and the test steps of the importing packages, about 240 more, the rest carried; a newer publish of the same -package supersedes the older one's rebuild pipelines, so two publishes close together run the rebuilds once. The first -pipelines after G's delivery run every module's lint and tests once, since `kept` starts empty (ADR 0666 §21): about -120 modules, one round of the build capacity, taken in the `check` stream's order; from then they run only what -changed. The steps go on the build seats' stream `check`, -after `delivery` and before `recheck`, within each node's build capacity (ADR 0525 §4, §5), and a run on request on the -same stream. How it is checked: live, the build nodes' time per day in the week after G, read from the build modules' -step keys and the builder's build log, against the checker's last week (test 99). - -### How milestone G itself is checked and delivered - -- §87 **Under the clauses as they stand.** Milestone G is checked and delivered under ADR 0656 §5 and the clauses it changed - as they hold before its delivery: the acting parts (the indexer, the build modules, the assigner, the grants part, - the node-engine, and the checker, which makes G's own check) act from the milestone's heads, each first in its step, - in the order of their ordering edges; the trunk-only want, the checker and the Approve per merge as ADR 0661 leaves it - govern this delivery, no exemption is asked and no delivery goes ahead of it (ADR 0667 §2, ADR 0644 §1). -- §88 **Its other parts as any module.** mesh-sdk's small object store package is a package published before the modules - that use it build; the manifest validator moves with the indexer; the runner is placed like any module and acts only - from the next change on; mesh-delivery's `contracts` and `sdk` move under its `modules/` folder (ADR 0667 §3). -- §89 **Readers first** (ADR 0667 §4): an edge of kind 4 each, which the delivery plan derives: the indexer's manifest - validator for the seat definitions' new fields (`callers`, `acts`, `subject`, `keep`) before any definition using - them; on each node the node-engine that reads `form` and `catch-up` before any manifest writing them. -- §90 **The stream maker first** (ADR 0667 §5): the grants part, which makes each kept seat stream and its durable - consumers, before the parts that hear a kept stream. -- §91 **The bus module first** (ADR 0667 §6): the bus module, with `-objects`, the sent store and the grant of each - state's object store to its owner alone, before the modules requiring them, the grants part writing that grant from - the milestone's heads. -- §92 **A bus restart is said first** (ADR 0667 §7): a restart of the bus its new version needs is said to the operator - before it starts. -- §93 **A gap is built, not worked around** (ADR 0667 §8): an order the delivery plan cannot derive from the edges is - built in the delivery system within G, never a step done by hand. -- §94 **A node-engine of the version before applies what it can read** (ADR 0667 §9): it applies every want whose - manifests use no field it does not read, and a want that uses one waits, by the reader edge above, for the new - node-engine on that node. -- §95 **The new assigner and the new merge switch on the first registrations** (ADR 0667 §10). The new assigner, which - writes a want only from a passed pipeline, is delivered before the forge module's new `merge-pull`, which asks no - Approve, and before the runner: an edge of kind 4 from `mesh-delivery-assigner` to each, declared in their manifests' - `needs` since nothing else says it (the design's choice: without it a merge made with no Approve could reach the old - assigner, which writes wants from the trunk with no pipeline). Both switch on one fact, the first registrations - written, said as `registration-changed`: until it the new assigner keeps the trunk-only want and the new - `merge-pull` refuses every merge, naming the missing registrations. -- §96 **Until then, the operator merges a fix at the forge** (ADR 0667 §11). Until that fact an ordinary change waits for - it, and a fix, an indexer fix included, is merged by the operator's own hand at the forge (ADR 0661 §3) and - delivered under the trunk-only want still in force; `indexer_seed_again` asks the first registrations again. Before - the milestone's delivery every merge goes as ADR 0661 has it. -- §97 **What is removed, and when** (ADR 0667 §12). The checker's buckets and bus account and the gate's forge merge token - are removed only after the first pipeline after G's delivery has delivered a change end to end (test 63), the runner - then making every check in the checker's place (ADR 0666 §19), and only on the operator's word, asked once for all - three. The checker then leaves the manifest validator's list of foundation parts (ADR 0666 §4) and is no longer - placed, so no running module declares its buckets and they - are orphans `orphans` lists; they go through ADR 0595's `drop` on that word, after the stream snapshot `drop` takes - (ADR 0595 §4, §5, §10), kept 90 days as the delivery records are; a restore from that snapshot is tested before the - word is asked. The runner is on that list from G's delivery. -- §98 **Told to the operator before it starts**: G's own merges are asked as ADR 0661 has them (ADR 0667 §2, §11); the one - ask of the seed registrations; the bus's restart (ADR 0667 §7); and later the one word for the checker's removal - (ADR 0667 §12). -- §99 **How it is checked.** Tests 94 to 97, and 100 for the assigner's edge. - -### Choices this design makes under ADR 0666 and ADR 0667 - -| Choice | Why | -|---|---| -| §100 The registrations in the indexer's old `state` section with their history on an old stream, capped at 64 MiB, kept for the life of the mesh | the indexer is a foundation part (ADR 0619 §1 to §3, ADR 0666 §4); a registration keeps every version with who and on whose Approve, and nothing derives it again | -| §101 The runner a foundation part, its kept data in old sections, on the manifest validator's list | the bus module's own delivery waits on its pipeline (ADR 0666 §4) | -| §102 The want's compare-and-set keyed per module and watched branch | source revisions are kept per watched branch, so two branches' revisions have no order; comparing across them would let `main` refuse `beta`'s want (ADR 0666 §9) | -| §103 A second start of a pipeline for the same source revision and module, on `take-in.taken` and `package-published`, writes nothing, by the pipeline key's compare-and-set | the runner starts on both events (ADR 0666 §13, §15); the key it already keeps judges a start in one place, so neither producer knows of the other | -| §104 A refused new indexer revision holds later merges on its branch until a revert lands | the validation runs after the merge and before take-in (ADR 0666 §6), so the refused revision has landed; taking later merges in past it would deliver them on a graph its own validator refused | -| §105 Each check at its row's level, every requirement R1 to R18 keeping its named test | the record's rows name the levels; the test budget and one check per release bound what runs | -| §106 The approval asked as the operator-channel's accepted kind `ask` on its work queue | the seat has no verb `ask`: an ask is its accepted kind, by the asker's own name (design 46 §10) | -| §107 The gate no foundation part, its `source` in the old section it already has | the bus module's delivery waits on no act of the gate's; only the key's layout changes | -| §108 The verbs `register`, `policy`, `watch`, `unwatch`, `registrations` and the event `registration-changed` on `mesh-catalog` | one verb per trusted act, each its own ask naming what moves; readers follow the event (ADR 0596) | -| §109 The seed of the first registrations proposed as one ask, with the statuses the check judged until then and approval `bypass`, raised again by `indexer_seed_again` | registering is a trusted act (ADR 0666 §3), and one ask for the whole seed is how placement was adopted (ADR 0567); a declined or expired seed must not leave the pipelines stopped with no way on | -| §110 Code review refused as `required` until forge accounts are bound to bus identities | a review is refused from its author (ADR 0666 §22), which cannot be told until then | -| §111 The seat `git`'s read verb `branch` and event `branch-moved`, kept on its seat stream | registering reads a branch's protection through the seat `git` (ADR 0666 §3); a branch moves by a merge or an admitted push; a stream keeps a move said while the bridge is down | -| §112 The watchman's finding `pipeline-stands` for a step or a head still five minutes, cleared by the step's end or the head's `pipeline-started` | the record's five minutes (ADR 0666 §24), one finding for both, timed from the runner's events | -| §113 The runner's finding `pipeline-failed`, the author's for two hours, then the operator's | ADR 0666 §24 with ADR 0505 §5, §6 | -| §114 A module's watched branch and its `build` in the placement key, set by a proposal | a placement names the branch and a return is one value in it (ADR 0666 §6, §24); every placement change goes by a proposal (ADR 0567) | -| §115 The gate's `source.`, its verb `source` and event `source-moved` | one source revision per watched branch (ADR 0666 §6), read through the seat (G's prepare, the indexer's watch of the gate's `source`) | -| §116 The build seats' verb `steps` and accepted kind `step` on `check`; the assigner's kind `step` on `mesh-wants`; `step-ended` with the node in its subject | steps are asked on the serving seat's work queue (ADR 0666 §7); the `check` stream already orders after `delivery`; a node's account says only its own node's ends (ADR 0363 §2) | -| §117 The outcomes, with `accepted`, `superseded` and `cancelled`; an approval expired or refused ends `declined`, replaced `superseded` | every end a step can have is said as itself (ADR 0666 §10) | -| §118 `approval` asked last, its `about` the module and the branch | the operator approves a commit the mesh already judged, the point ADR 0661 §3 kept in one place; one ask per module and branch, a newer commit's replacing it | -| §119 A superseded pipeline's asks dropped by `drop`, and a pipeline superseded only when its module's fingerprint changed | a cancelled pipeline must leave no ask on a stream (ADR 0664 §4; G's prepare); a module the commit did not change has nothing newer to run (ADR 0666 §9) | -| §120 A group's wants only on `group-ended` | a group is delivered whole or not at all (ADR 0666 §9; research 086, R5) | -| §121 The six kinds derived from existing words, `needs` per artifact only where nothing says it | one form of each fact (ADR 0656 §6); the catalog entry's `needs` already exists from G | -| §122 The cycle rule in the indexer's graph | the graph is the indexer's, and the checking program goes with the checker | -| §123 The builder's seat `mesh-builds` and its event `package-published` | the record has the builder say the publication as an event of its seat (ADR 0666 §15); its waits move only when a later change gives the seat its verbs | -| §124 Each state's object store `-objects` through mesh-sdk's small package, sent objects readable by a standing grant derived from the manifests, and the package's two tools | one object store per state, granted to its owner; the sender's declared callers and consumers read what it sends (ADR 0666 §16); only its users import it, so nothing else is rebuilt | -| §125 The `run` ask published by the runner's account and the indexer's; the verb `run` called by the sessions and the operator only | the steps and the validation run code from the change, in the sandbox only, and each asker reads only its own step keys (ADR 0363 §2); a run publishes tagged packages, so nothing else may ask one | -| §126 The checker's step `check` becomes `pipeline`; `kept` starts empty | the runner is an acting part in the checker's place (ADR 0666 §19); a step's result is taken only from its seat holder (ADR 0666 §21), so no verdict of the checker's is taken in, at the cost of one round of every module's tests | -| §127 The field `callers` in a seat definition replaces the holders' trusted `callers`, a missing field admitting the holder roles and the operator only | the record makes the definition the grants part's one source (ADR 0666 §20); a field left out must open nothing | -| §128 `widens` in `pipeline-ended`, computed by the running runner over every part serving a step, passed on by the teller | a widening is said and nothing refuses it (ADR 0666 §23, §26); a change must not compute its own | -| §129 `merge-pull` judged from the forge module's own `failed-steps`, heard from `step-ended`, any run at the head's fingerprint counting unless a later rerun of that step passed, the protection read again on every `branch-moved` | the record has it judge from the events it consumes, never by a call, so a head merges while the runner is down (ADR 0666 §11, ADR 0596) | -| §130 The new assigner before the new `merge-pull` and the runner, by `needs`, both switching on the first `registration-changed` | a merge with no Approve must never reach an assigner that writes wants without a pipeline (ADR 0667 §10) | -| §131 The forge bridge's finding `branch-unprotected` | a move of a branch whose protection was removed starts no delivering pipeline and is said (ADR 0666 §6) | -| §132 The verb `recovery` of `mesh-delivery` | a recovery is named by its author as a fact the bus keeps with who said it, never read from text (ADR 0666 §26, research 086 R10) | -| §133 No forge status for a pipeline | the step results live on the bus (research 086 R10), shown by the runner and the delivery record; a status would be a second copy outside it | -| §134 The runner's `pipelines` kept as long as the delivery record that cites each (90 days), a superseded, run-on-request or uncited one 7 days, its step log 90 days by age, its outputs 7, keyed by source revision | a pipeline is read back as long as the record that delivered it; one nothing delivered has no trace to serve after a week; about 300 pipelines a day must fit its caps, so they are 256 MiB and 128 MiB | -| §135 The forge module's `failed-steps` kept until no head it was seen at is a watched branch's head or an open pull request's | an entry judges only a head that can still be merged; once neither names it, nothing can be refused by it | -| §136 The gate's `source.` deleted when the branch's last watch goes | a branch no registration watches makes no revision, so its key would name nothing | -| §137 The sent store kept 7 days by age, with no person's word | an object sent carries one reply or event and is transport, not kept data (ADR 0595 §4); its readers fetch it within a call's bound or a consumer's lag | -| §138 A replaced object listed `key gone`, as a deleted key's is | no key names it, and one listing lets the drop tool take both on a person's word, as ADR 0595 §4 and ADR 0666 §16 have it | -| §139 The checker's removal after the first end-to-end pipeline, on one word, its buckets through `drop`, the snapshot kept 90 days | ADR 0667 §12 asks one word for all three; 90 days is the delivery records' retention, the longest a trace reads back | -| §140 The pipeline steps on the `check` stream after `delivery` | a delivery's build is never held by a pipeline, as a check never held one (ADR 0525 §4) | - -## The delivery record, and what the gate owes the forge - -### The delivery record - -*ADR 0656; ADR 0595, ADR 0596, ADR 0597, ADR 0603 §7, ADR 0493 §3, §11, ADR 0633 §4, §5, ADR 0441 §1, ADR 0548 §8, -ADR 0564 §2, ADR 0363 §2.* - -§141 From research 085's milestone the gate, holder of the seat `mesh-delivery`, keeps one **delivery record** per -milestone's delivery, a ledger instance (ADR 0595's template `ledger`, ADR 0597) it alone writes (ADR 0656). It -holds: - -- §142 **the group's state**: the heads, the verdicts, the checks and the asks, which `merges` holds today; from milestone - G's delivery the pipelines' `step-started`, `step-ended` and `pipeline-ended` in place of the verdicts, the checks and - the asks, kept as long as the record (ADR 0666 §9); -- §143 **the trace**: the milestone, its issues, its pull requests, their commits, and the graph revisions the delivery - moved between; -- §144 **what happened to each entry on each node**: for every entry of the graph difference, taken in, built, checked, - composed, wanted, running healthy, held (with the hold's reason, so a reader of the record sees why an entry stands - still without asking its holder), failed (with what failed), or away (ADR 0656). - -§145 It holds no tiers, and only the gate orders work by it, so it is no second copy of the keys, the objection research -063 had to a stored plan (research 085, Findings 10). Its fields, retention and tools are this design's choices under -ADR 0656. - -- §146 **The outcomes arrive as events, through a durable consumer of the gate's own.** The gate's manifest `consumes` - each step event of *What each step says* (below), and `wait-held` and `wait-ended` of each holder. The consumer - resumes from the last event applied; an event applied twice changes nothing; an event is applied only when it comes - from its owner's account; and running healthy is taken from the node-engine seat's `running` event, never from a - have read across the seam (ADR 0596). -- §147 **It ends when every present node runs its entries**, and the gate says `record-ended` on the seat `mesh-delivery` - with the record's milestone and its nodes away. It names the nodes away, each with since when, and ends without - waiting for them, as a node away holds nothing (ADR 0603 §7). -- §148 **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. 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. -- §149 **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. -- §150 **An Approve given holds at its heads, until G's delivery.** An Approve the operator gave for a group's heads holds while those heads - stand: a check of the same heads asked again, after a main move or for any other reason, records its verdict in - the record and asks the operator nothing again (issue 1972). The Approve is withdrawn only as ADR 0441 §1 has it, - when a member stops being mergeable, and a carried verdict is read as current (ADR 0548 §8). From G's delivery no - Approve is asked per merge; an `approval` step's answer is the runner's, kept in the record as its step's end - (ADR 0666 §10). -- §151 **From G's delivery, a record for every landed merge.** A session merges through the seat `git` and the gate merges - nothing, so every merge is one the gate did not make: the gate opens a record for each source revision of a watched - branch the indexer takes in, a group's members in one, and `forge-made` no longer marks a record apart. A merge its - author names a recovery is marked `recovery` (*A recovery fix is an ordinary merge*). -- §152 **A small fix has a record of its own.** A small fix is a milestone of one issue (ADR 0493 §3), so it has a - delivery record like any milestone; nothing is merged outside a released milestone (ADR 0493 §11). -- §153 **It pins the graph revisions it points at.** The gate says, as the seat `mesh-delivery`'s events `pinned` and - `freed`, which catalog and assignment graph revisions a record starts and stops pointing at, and as - `heads-replaced` that a candidate's heads were replaced or its pull request closed. Only the gate's account may - publish them (ADR 0363 §2). The indexer and the assigner hear them through a durable consumer each, declared in - their manifests' `consumes`, which resumes from the last event applied after a restart, so a `freed` said while one - of them was down is still applied; neither reads the gate's state (ADR 0596; *Two graphs, one source of truth*, - below). -- §154 **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. -- §155 **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. -- §156 **The switch carries today's state in, once.** At the new gate's start, `merges` and every group in flight become - records, and from then on the gate writes `merges` no more; `turns`, which nothing writes while the check gates no - merge, is dropped with the hold (*Nothing merges into a repository under check*). 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 delivering milestone G again after a revert makes no second record and asks - nothing again. A revert of the gate is the delivery system's restore of its kept data (*A module's kept data on the - bus, backed up and restored with its delivery*). -- §157 **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`. -- §158 **What the gate owes the forge is said, not watched, and a merge made at the forge has a record too** (*What the - gate owes the forge*, below). -- §159 **Its tools**, against the four lines a module's tools answer: - - | Line | The gate | - |---|---| - | read everything it holds | `gate_record` (one record whole: the group's state, the trace, each entry's outcome per node, the nodes away), `gate_records` (every record with its fill against the value limit, and the ledger's fill against its cap), `gate_consumer` (its durable consumer: the last event applied per family, the events waiting, the age of the oldest) | - | do by hand what it does itself | `gate_record_repair` (for one record, asks each owner for the outcome of each entry missing one through that owner's read verb: the indexer's `take-in` read, the builder's build read, the checker seat's check read (from G's delivery the runner's `show`), the seat `mesh-wants`'s `entry`, and the node-engine seat's verb `composition` of that node; applies the answers as the consumer would) | - | recover without help | `gate_record_end` (end a record left open, a node gone for good or an entry that never runs, with the reason, recorded in the hand-act log of the seat `mesh-hand-acts`) | - | one seat, many tools | the seat `mesh-delivery`'s verbs, with `groups` answering a group's members and the acts said for it | - -- §160 **How it is checked.** In the gate's whole-module test: the gate stopped and restarted in the middle of a delivery - leaves the record complete with no outcome counted twice; the events `pinned`, `freed`, `heads-replaced` and - `record-ended` are said; a `freed` from an account other than the gate's is refused by the bus; a group approved - before the switch is not asked again after it, nor after milestone G is delivered again following a revert; a record ends once every present node runs its - entries, naming the nodes away; a held and a failed entry are kept with their reason; a record open past 24 hours - raises `record-open`, cleared by `record-ended`; a node away that wakes adds its outcomes and is no longer shown - away; a group's heads approved and checked again ask the operator nothing again; a small fix's merge gets a record - of its own; a record past its retention is removed and frees its revisions; the tools read a record, list the - records with their fill, and a repair completes a record missing an outcome through the owners' read verbs. In the - forge bridge's whole-module test, a group's members are read from `groups` and its end published again from it, and - a read of `merges` by its account is refused. Live: one call of `gate_record` traces an issue of the registry - milestone to each node's running build, also after six later merges; a node away is named with since when and its - outcome taken on waking; a record of 30 modules on four nodes stays under 256 KiB; a group checked again at old - heads after a main move is not asked of the operator again (issue 1972). - -### What the gate owes the forge - -*ADR 0596 §1, §2, ADR 0656 §7, ADR 0437 §8, ADR 0441 §5, ADR 0633 §1, §4, ADR 0363 §2, ADR 0564 §2; from research 085's -milestone, this design's choices under ADR 0596 and ADR 0656 §7.* - -§161 The forge bridge today performs what the gate's states `merges` and `owed` ask (a merge, a status, the delivery's -view, a comment, a line on the commit's note) by watching those keys (ADR 0437 §8), and the forge module writes the -checker's `mesh/check` from a watch of the checker's state `owed`: reads of another module's bus state that ADR 0596 -§2 ends when their owner moves. From research 085's milestone: - -- §162 **Every act is an event, under one id.** The gate says each act it owes as the event `owed` of the seat - `mesh-delivery`, on `mesh.seat.mesh-delivery.event.owed..`, carrying the act, the pull request, - the head and an id; the checker says its status the same way, as the event `owed` of the seat - `mesh-delivery-checker` on `mesh.seat.mesh-delivery-checker.event.owed..`. Only the owner's - account publishes each (ADR 0363 §2). The subject per repository and pull request lets the bridge, a person and a - test read one pull request's acts alone. The seat events are kept on each owner's stream `owed`, 7 days and at most - 64 MiB (an estimated 20 deliveries a day, with about 15 acts of about 1 KiB each, is about 2 MiB over 7 days). -- §163 **Every act is safe to repeat at the forge.** A status and the delivery's view replace what stands; a merge of a - pull request already merged at that head is answered performed; a comment and a line of the note carry the act's - id, and the seat `git`'s verb skips an id the pull request or note already holds. This is chosen over recording the - id before the act: an id recorded and then not performed (the bridge stopped between the two) would be lost, while - an act repeated changes nothing. The bridge's own state `performed`, a key-value state in the new form (ADR 0595), - form 1, one key per id with the outcome and when, kept 7 days as the stream is, its size the one its template fixes - (ADR 0595 §3), is therefore a - saving and a read, never what keeps an act from happening twice. -- §164 **Every outcome is said, and the gate records it.** The bridge says each act's outcome on its own stream `forge` as - the event `owed.ended`, with the id and one of `performed`, `refused` (the forge's own words), `could not be - performed` (the forge did not answer within the call's bound, with the error) or `undecodable` (an act it cannot - read, with why): an act it cannot decode is said, never only logged. The gate hears the stream `forge` through its - durable consumer, writes the outcome into the delivery record, and says an act's id is answered only once an - outcome is recorded. The checker does the same for its own acts: it hears `owed.ended` for its own ids, records each - outcome in its own state (it watches only its own state), says again at its start, under the same id, every act of - its own still unanswered, and lists one unanswered past 15 minutes in its own stalled read. -- §165 **What is unanswered is said again, and stalls by a bound.** At its start the gate says again, under the same id, - every act its records hold unanswered. An act unanswered 15 minutes after it was said is listed by `stalled`, which - the condition keeper raises as the stalled line (ADR 0421 §7, ADR 0564 §2): fifteen minutes is many times a - forge call's time, and short beside the check's hour. -- §166 **A group's merges follow one another, until G's delivery.** The gate says a group's next `merge-pull` only once the previous member's - merged message is heard (ADR 0441 §5), so a member the forge refuses holds the rest, and none is merged ahead. -- §167 **From G's delivery the gate owes no merge, and the checker nothing.** A session calls `merge-pull` itself (ADR 0666 - §11), and a group's members land one by one, the group ending when every member has (*Groups*); the checker's `owed` - goes with the checker, and the runner owes the forge nothing, a pipeline's result living on the bus. The gate still - owes the delivery's view, a comment and the note's line, performed by the forge bridge as above. -- §168 **The bridge catches up from the seat.** At its start the bridge resumes its durable consumer and also reads - `groups`, which answers, per group, the acts the gate said with their ids and their recorded outcomes; an id said - and not answered is performed. Its tools: `bridge_owed` (the ids it performed with their outcomes, and its - consumer's backlog: the last event applied, the events waiting, the age of the oldest), `bridge_owed_again` - (perform one id again now) and `bridge_owed_drop` (drop one id with its reason, written in the hand-act log of the - seat `mesh-hand-acts`, and said as `owed.ended` with the outcome `dropped`). -- §169 **The switch carries what is owed.** At the new gate's start (*The switch carries today's state in, once*) every key - of `owed` and `merges` the old bridge has not performed becomes an act of its record, with an id made of the key and - its revision; the new bridge counts as performed every id whose key and revision the old bridge's own bookkeeping - names, so nothing is done twice and nothing dropped. A merge handed to the forge during the switch is such an act: - its merged message finds the record holding it, so it opens no `forge-made` record. -- §170 **A merge made at the forge has a record too.** A merge the forge reports for which no record holds a `merge-pull` - of that pull request at that head (a person merged it in the forge's own page, as on 2026-11-07 and 11-09) is taken - in like any other merge (*Taken in by the indexer it names*), and the gate opens a delivery record for it, marked - `forge-made`, with no Approve, so its trace and its outcomes on the nodes are kept like every delivery's and no - later merge waits behind a merge the gate never heard. The gate says it as the delivery's `transition` with the why - `forge-made`, which the teller passes on to the operator (the operator, 1921's note of 2026-11-07: "a merge made in - the forge is taken in like any other"). -- §171 **How it is checked.** In the forge bridge's whole-module test: an `owed` said while the bridge is stopped is - performed once after its restart; the bridge stopped between an act and its `owed.ended` performs it again on its - restart and the forge holds it once (the comment and the note by their id); an act said twice is performed once; an - undecodable act is said `undecodable`; `bridge_owed`, `bridge_owed_again` and `bridge_owed_drop` answer, the drop - writing the hand-act log; its account cannot read the gate's or the checker's state. In the gate's: an act left - unanswered is said again at the gate's start under its id and listed by `stalled` after 15 minutes; a group's second - `merge-pull` is said only after the first member's merged message; the switch with a merge handed and not yet - merged makes no `forge-made` record; a merged message with no record's merge opens a `forge-made` record, said as - `transition` with why `forge-made`, and a later merge in the same repository is taken in behind it. Acceptance - tests 53 and 51. - -## The state table, from milestone G's delivery - -§172 **From milestone G's delivery the gate keeps no merge states.** With no check before a merge and no Approve per merge -(ADR 0666 §10, §17), nothing is `proposed`, `ready`, `rejected` or `held` before a merge: a commit's states are its -pipeline's steps, kept by the runner (*Pipeline steps and the policy*). The table, the gate's `table`, `recheck` and -`close` and the one key per pull request go with the check; `stalled` lists the acts owed to the forge past their 15 -minutes and the records `record-open` names. - -## 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 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.* - -§173 From research 085's milestone. One graph answered two questions, which modules exist and which version runs where, -and one module running as several placed versions would have given one entry two shapes and one key two writers; two -graphs answer them, the second computed from the first, so there is one source of truth (ADR 0656; research 085, -Findings 11). How each is kept, read and retained is this design's choice under ADR 0656. - -``` - manifests ──► indexer (mesh-catalog) ──► catalog graph revision ──┐ - ├──► assigner (mesh-wants) ──► assignment graph revision - placements (mesh-module-assigner) ───────────────────────────────┤ │ │ - node capabilities (node-engine seat) ────────────────────────────┘ │ └─► wants, from a trunk revision only - ▼ - a change's assignment graph − the current one = its graph difference = its delivery -``` - -- §174 **The catalog graph is the indexer's.** The indexer, holder of the seat `mesh-catalog`, keeps the modules as their - manifests declare them; it is today's module graph, and *module graph* stays its other name (ADR 0656). At each - revision it holds one entry per module: its bases, its packages, the toolchain version and recipe version of the - build module that builds it as ADR 0603 §2 has them, its provides and requires, its invokes and serves, and its - fingerprint, with the rules the seats declare (*An acting part's new version acts first*, below). -- §175 **The assignment graph is the assigner's, computed and never written by hand.** The assigner, holder of the seats - `mesh-module-assigner` and `mesh-wants`, computes the module versions placed on nodes, with the edges between - versions, from the catalog graph, the placements and the nodes' capabilities, and nothing else writes it - (ADR 0656). Each entry is one module version on a node; its edges are provisioning, contribution, and the acting - parts' versions that are to act on it (ADR 0656), whose rules and versions come in with the catalog graph. It is - kept, but it is no stored plan: it has one writer, no part orders work by a stored plan, and any revision is computed - again from the inputs it names and compared with the one kept, by a tool (below), so a kept revision is a checked - derivation, never a second source of the kind research 063 removed (research 085, Findings 5). -- §176 **Each graph is kept per revision, and each revision names its inputs** (ADR 0656). The indexer keeps a catalog - graph revision for each source revision it takes in; the assigner keeps an assignment graph revision for each change - of its inputs, naming the catalog graph revision, the placements and the capability readings it was computed from. - Both parts are the foundation (ADR 0619 §1), so both keep their revisions as objects declared in an `objects` - section in the old form, each with a cap it declares there, never in the new form (ADR 0619 §2), and neither waits - on the bus module to start; the manifest validator refuses either manifest that declares them in the new form - (ADR 0619 §3). An assignment graph revision is announced by an event of the seat `mesh-wants` and served by its - verbs. This replaces ADR 0603 §2's one key per module with five past values as the graph's form (ADR 0656, which - changes ADR 0603 §2); the keys are written no more, and a revert of milestone G restores them from their backup - (*Reverted*, below). -- §177 **A revision is made by a merge, and also with no merge.** The indexer makes a catalog graph revision when it - takes a source revision in. The assigner makes an assignment graph revision on each new catalog graph revision, on - the seat `mesh-module-assigner`'s event `assignments-changed` (ADR 0567 §5, ADR 0641), and on the node-engine seat's event - `capabilities-changed`, which a node-engine says when the capabilities it states in its node record change - (ADR 0614 §4), so the assigner follows an event and never reads a node record across the seam (ADR 0596). A - placement change is traced like a merge. At its first start with no revision kept, the indexer takes in the head the - gate's source names for every repository by its catch-up path and keeps that as revision 1, and its verb `revision` - answers that it waits until then (lead-47's ruling in G's prepare: a first start has no merge to make a revision). -- §178 **A revision is kept while a record pins it or it is current.** A revision of either graph, and the input revisions - it names, is removed when no delivery record pins it and it is not current: neither trunk's newest revision nor the - newest of an open candidate. Pins are counted per revision, so a revision two records point at stays until both - 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. -- §179 **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. -- §180 **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. -- §181 **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. 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. -- §182 **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). -- §183 **The want stays the one record a node reads.** The assigner writes `want..` from a trunk - revision's assignment graph only; a node-engine reads its want, never the assignment graph, and the have stays what - it runs (ADR 0656, which changes ADR 0571 §8). -- §184 **From G's delivery, per watched branch and between artifacts.** The catalog graph is kept once per watched branch, - each revision naming its branch, and its edges run between artifact references, of the six kinds of *Dependencies - between artifacts* (ADR 0666 §6, §14). The assigner computes a node's entries from the catalog graph of the branch its - placement names, and writes a want from a revision of that branch whose `required` steps passed, in place of a trunk - revision's alone (ADR 0666 §9, which changes ADR 0656 §2). -- §185 **Reverted.** Only the new form is written: the indexer, the assigner and the gate keep nothing of ADR 0603 §2's - keys or of `merges` and `turns` beside the new forms, since nothing reads them after milestone G. A revert of - milestone G is a change like any other, checked and delivered, and the delivery system restores what it must - (*A module's kept data on the bus, backed up and restored with its delivery*, under *A backup before the apply*). - Milestone G changes the form of these kept data, and raises their `form` (a kept data declaring none reads as form - 0, so G's own move takes their restore points): - - | 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`; the states `holds`, `engine-reads` and `hold-bounds` of its mesh-wide hold | catalog graph revisions as objects, form 1; `modules` no longer declared, nor its tool `indexer_read_modules` or its view; the mesh-wide hold, its three states and its tools `indexer_holds` and `indexer_engine_reads` gone, the wait per node being the assigner's (*Composing waits per node, by the assigner*); every reader of it reading the catalog half (*Every other reader of which modules exist*, under *The revision view*, below) | yes: `modules` and the hold's three states as they 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 | - - The wants keep their form: their keys and values are unchanged, only what the assigner computes them from changes, - so G raises no `form` on them and a revert restores none. Restoring the wants would send nodes back to older - 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. 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 - -- §186 **The difference names everything a plan named.** For a change (a pull request's heads, a delivery group's - heads, or a merge), the indexer gives its catalog graph and the assigner its assignment graph. Compared with the - current revision, the difference names the new versions, the moved edges, the touched nodes and the order, read off - the edges, and no plan is stored (ADR 0656). The assigner serves it by the seat `mesh-wants`'s verb `difference`, - for two revisions of the assignment graph or for a candidate against trunk. ADR 0603 §6's tiers, as ADR 0633 §5 - changes it, are a view worked out from that difference (ADR 0656, which changes ADR 0603 §6), read through - `difference`, with the waits and holds read through their owners' tools and the pending revisions through - `indexer_pending`. -- §187 **The preview is a candidate's difference.** Issue 1874's plan preview of a group before its merge is its - candidate's difference, read by one call of `difference`, and needs no code of its own: a candidate's graphs are - computed for its check anyway, so the preview costs nothing more. -- §188 **A changed fingerprint with no difference is refused by name.** When a module's fingerprint in the change - differs from the running one while the difference is empty, the assigner refuses it, naming the module, and the - check fails naming it. -- §189 **A candidate's graphs are kept apart, and dropped with its heads.** A candidate's two graphs are computed per - check, kept apart from trunk's, and dropped on the gate's event `heads-replaced`, said when the candidate's heads - were replaced or its pull request closed. A candidate costs a computed graph per check. -- §190 **Only a trunk revision writes wants**, whether a merge taken in or a placement or capability change made it; a - candidate's graph writes none (ADR 0656). From G's delivery a want comes from a revision of the watched branch a - placement chose whose `required` steps passed, and a candidate is a run on request's: the indexer builds it on the - runner's `run-started` and drops it on that run's `pipeline-ended`, the gate's `heads-replaced` no longer making - candidates (ADR 0666 §9, §18). -- §191 **Each part acts on its own entries.** The builder, the build modules and the grants part each act when their own - entries in the new revision differ from what they run (ADR 0656), reading them through the seat `mesh-wants`'s verbs - and events, never its bus state (ADR 0596), and naming no other module (ADR 0656). The node-engine acts on its want - alone, as it does today: the want is the one record a node reads, and handing every node the assignment half would - have each node read every other node's placements. -- §192 **A node asleep catches up when it wakes.** A node away holds nothing and catches up by its wants (ADR 0603 §7); - a want's new form reaches only a node-engine that says it reads it (ADR 0614 §4, ADR 0618 §2). -- §193 **No delivery goes ahead of a change.** A change is delivered as its own difference, its acting parts' new versions - first in their own steps (ADR 0656); the one exception is the delivery before research 085's milestone (*The - bootstrap delivery, and the one milestone after it*, under *Milestones*; ADR 0644). - -### The revision view - -- §194 **A pair, each half served by its one owner** (ADR 0656). For a trunk revision, or for a candidate's heads before - any merge, the indexer serves the catalog half by the seat `mesh-catalog`'s verb `revision`: the catalog graph, the - heads, the seat definitions, the manifests' verdicts and the fingerprints. The assigner serves the assignment half by - the seat `mesh-wants`'s verb `revision`: an assignment graph revision, named by its own revision and naming its - inputs, the catalog graph revision, the placements and the capability readings. A part's view for the current - revision is the newest assignment half and the catalog half it names. `seat-definitions` stays the seat list's verb - (ADR 0636 §4), and the catalog half carries the same definitions at its revision. -- §195 **Every part takes a first view, then follows the events.** The checker, the builder, the grants part and the - indexer's own later passes take a first view from the two verbs and then follow the two seats' events, never a list - compiled into their build nor another module's bus state (ADR 0656, ADR 0596). The node-engine reads only the catalog - half, for the seat definitions it composes with, and its want; it never reads the assignment half (*Each part acts on - its own entries*, above). Of the view the assigner reads only the catalog half, never its own output back; its other - inputs stay the placements and the capabilities (ADR 0656). A running part acts only on a trunk revision's view; a - candidate's view is read only by the parts that judge a candidate, inside the check its manifest validator and - composer, and the indexer's candidate pass (ADR 0656, which changes ADR 0636 §4 in that the candidate pass joins - them). From G's delivery a running part acts on a revision of a watched branch as on a trunk revision, and a - candidate's view is read only by a run on request's steps and the indexer's candidate pass (ADR 0666 §6, §18). -- §196 **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*, - 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). 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 has no view - today; milestone G adds there a view follower with its read and ask-again tools, beside the proved caller, a - verb's `callers` and a seat's publish, 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. Its cost: a release of the TypeScript side rebuilds every TypeScript module, which - the delivery carries like any change of a package its users build in. - - **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 0663, which changes ADR 0621 §1, §2 and - ADR 0615 §3; accepted); 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). -- §197 **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*). -- §198 **The indexer reads every definition before it judges.** At its start the indexer reads the seat definitions of - every contracts module's trunk through the forge (the seat `git`'s verbs) before it judges a manifest or answers - `seat-definitions`, and only then follows the forge bridge's stream; a definition merged while it was down is - therefore never judged against a list that lacks it. The design's choice under ADR 0636 §1 and §4, as the operator - asked it (1921's note of 2026-11-07: "the indexer reads its seat definitions at start"). How it is checked: in the - indexer's whole-module test, started with a definition on a contracts trunk that no event announced, the first - manifest claiming that seat is judged with it. -- §199 **Who holds a seat is applied by one rule, from what each node runs.** The node-engine and the grants part decide a - seat's pass by mesh-sdk's `SeatsToPassOn` (ADR 0660 §3), reading the manifest of the build each assignment runs - (ADR 0660 §2). What each node runs is heard, never asked: each part keeps it from the node-engine seat's events - `running..` (*What each step says*), through a durable consumer of its own declared in its `consumes`, - so a pass is decided with no call to a node that may be away. The seat's stream keeps the last message per subject - with no age limit, so the last `running` of every module on every node stays on it however long ago it was said, - and each part's durable consumer starts from the last message per subject, never from now: a consumer made after a - node's last `running`, and two parts' consumers made at different times, hold the same builds. The event carries the module, the fingerprint and the - digest of the build's manifest, which is the indexer's manifest of that build (ADR 0660 §2's "the indexer's - manifest of it"), read by digest from the catalog half; the builder's done key is not needed, since the event names - the digest it would give. A node that has said no `running` for a module since the part's consumer began counts by - the trunk's manifest, as ADR 0660 §2 has it where the running build cannot be read, and both parts apply that same - fallback in the one function (ADR 0660 §3). Never another node's have key, its want or the assignment half (ADR 0596 - §2; *Each part acts on its own entries*). The design's choice under ADR 0660 §2: the node that runs a build says what - it runs, and an event kept by a durable consumer answers when that node is away. How it is checked: test 56. -- §200 **A part with no view says it waits.** It names the revision it has no view for, acts on nothing new, and keeps - running what it runs, as ADR 0636 §1 has a reader of the seat list do; the checker, the builder and the grants part - wait too, and wait for the view, not only the seat list (ADR 0656, which changes ADR 0636 §1); the node-engine waits - for the catalog half as ADR 0636 §1 already has it wait for the seat list. Each answers in its runtime's first-view - tool (below) the revision it holds, the one it waits for and since when, and after five minutes without a view writes the finding - `view-waits` naming itself and the revision, as a reader of the seat list does, which the condition keeper raises - (ADR 0564 §2); it clears when the view arrives. Five minutes is the bound the seat list's readers already keep, so an - indexer or assigner down for a restart raises nothing. A part restarted while the indexer or the assigner is down - waits and says so. -- §201 **Each process knows its own build from its start.** The node-engine hands every process it starts the - fingerprint of the build it starts it with, and the process takes its own build from that, never learning it later - (ADR 0656, which changes ADR 0639 §7 from the indexer to every process). *The fingerprint handover*, below, says its - form. - -### What ADR 0656 leaves to this design - -§202 ADR 0656 leaves how each part is built to this design, under issue 1921; these four needed a shape of their own. - -- §203 **An acting rule's fields.** The seat's definition is where ADR 0656 has an acting part declare what it acts on. A - seat's definition gains one optional field `acts`, a list of rules, each with three fields of its own: `step`, one of `take-in`, - `validate`, `check`, `build`, `compose`, `grant` and `place`, and from G's delivery `pipeline`, the runner's, in - `check`'s place (ADR 0666 §19); `on`, either `every` (every module) or a `language` - naming the language of the modules it acts on, matched against what the manifest says its language is, as the - indexer's language map matches today; and `bound`, how long an entry waits in that step for the acting part's new - version (*An acting part's new version acts first*, below, gives each step's default). The definition format's own - schema (design 26) gains the field, and the format stays 1. The indexer reads it into the catalog graph, the assigner - into edges, and the checking program from the revision view. **Its cost in mesh-sdk:** the seat-definition reader in - mesh-sdk's runtime refuses a definition with a field it does not know or a format other than 1, so a reader built - before the field refuses every definition that carries `acts`, and a refused definition blocks new claims of that - seat only (ADR 0636 §3), never what runs. The runtime therefore learns `acts`, which rebuilds every module once - (ADR 0656, Consequences); that is the same rebuild the fingerprint handover makes, so the milestone rebuilds every - module once in all, and no other module's code changes. A definition carrying `acts` reaches only readers built with - that runtime: the seat definitions that gain it are members of milestone G's group, and every reader is rebuilt in - the same delivery; the format's reader itself is delivered before them, as a release of mesh-sdk's seat-definition - reader merged ahead of G's check (the operator's choice of a reader first, 2026-11-09; ADR 0667 §4). The bootstrapped - definitions every reader carries from its start (ADR 0636 §2), of which `node-backup` carries no `acts`, are made - again from the new definitions in the same change, and mesh-sdk's existing test that each bootstrapped copy equals its definition holds - them to it. -- §204 **One wiring of the seat list's reader.** The reader of the seat list and its two tools of ADR 0636 §7 are wired once, - by mesh-sdk's runtime (`go/seatlist`), for every part that reads the list; no part wires its own. Four delivery - parts (the assigner, the checker, the indexer and the grants part) each carry a hand-wired copy today, each with its - own pair of tools, and each copy would have to learn `acts` apart. The design's choice under ADR 0636 §7, which puts - the tools in the runtime bundle: one wiring learns `acts` once, and every part's two tools are the same, named by - their module, with the same callers (any person and the healers to read, the operator and the healers to read - again), the node-engine's among them. How it is checked: mesh-sdk's seat-list test registers both tools for a - fixture part; a search of the delivery parts' code finds no wiring of its own. -- §205 **The fingerprint handover.** The node-engine starts each process, a service or a container alike, with the - environment value `MESH_FINGERPRINT`, the fingerprint of the want it applies; mesh-sdk's runtime reads it once at the - process's start and holds it as the process's own build (ADR 0656). A process started with no value says `own build - unknown` in its runtime tool and its journal, and the indexer acts on it as ADR 0639 §8 has it. **Its cost:** one - change of mesh-sdk's runtime, which rebuilds every module once (ADR 0656, Consequences), and one of the - node-engine; the order between them is the composing step's wait of the node-engine (*Order without a person*, - below), so a module whose runtime reads the value is composed only by a node-engine that hands it. -- §206 **The build seat's ask that runs a program in the sandbox.** The checker asks the seat `node-build-go` for an ask - `run` on the build seats' `check` stream: one whole job on one node (ADR 0525 §3), which fetches the named heads, - builds the named program at them, runs it in the run step (ADR 0448 §2) with the input the ask names by its sha256 in - the asker's own state (the checker's `inputs`, a day), asked of the seat `mesh-delivery-checker`'s verb `input` and - checked against that digest, laid as a file, and writes the program's exit and its answer, carried whole, 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 from G's delivery the runner's and the indexer's (its validation before take-in), each under its own name. - Every step key of a `run` ask starts with the asker's name (`checker.`), 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. From milestone G the seat definition of `node-build-go` in `mesh-delivery-contracts` names it; B0 - brings the ask with its types in mesh-sdk's `go/delivery` and the grants check asks have today (*The bootstrap - delivery, and the one milestone after 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. 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. -- §207 **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 - for a build node` when no build node of the language took the ask within the bound, and `did not build` when one - took it and the build failed, so the operator knows whether to free a build node or fix the change. - -### Choices this design makes under ADR 0656 - -§208 ADR 0656 decides the direction and leaves every choice made in working it out to this design, each with its reason -(ADR 0656, Consequences; the operator, 2026-11-06 20:3x). The choices, and why: - -| Choice | Why | -|---|---| -| §209 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 | -| §210 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 | -| §211 No old form written beside the new; a revert restores the parts' kept data on the bus from a restore point taken before they moved, then they catch up forward | only the new form is looked after (the operator, 2026-11-06 21:3x), and a revert still works, as the delivery system's capability for every change of a kept data's form | -| §212 A module's kept data on the bus backed up before a `form` change and restored beside the live data at a revert, a missing restore point holding the revert | ADR 0404 covers only the items on a module's node, so the bus's kept data had no restore point; a restore never goes over live data, and a revert with nothing to restore must not start on the newer form unasked | -| §213 Only kept data whose `form` changes restored, the wants never; at a revert the node-engine last on its node, and the reverted assigner and gate only after the reverted indexer took in the trunk's head | restoring anything else would set back what was decided since; restored wants, or an assigner reading an indexer not yet caught up, would send nodes to older fingerprints; the node-engine of before may not know how to restore | -| §214 A restore point holding the consumers' positions and the catch-up tool the newer manifest declares, called after a restore | the reverted build then hears everything since as after any restart; the stream `forge` keeps 7 days, so what fell out of it is taken in from the trunk by the part's own tool | -| §215 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 | -| §216 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 | -| §217 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 | -| §218 `record-open` only when no other condition covers the record | the cause is already told once; a second condition for it is noise | -| §219 `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 | -| §220 The switch into records keyed by milestone and heads, safe to run twice | delivering milestone G again after a revert makes no second record and asks no group again | -| §221 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 | -| §222 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 | -| §223 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 | -| §224 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 | -| §225 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 | -| §226 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) | -| §227 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) | -| §228 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 | -| §229 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 | -| §230 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 | -| §231 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) | -| §232 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 | -| §233 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 | -| §234 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 | -| §235 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 | -| §236 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 | -| §237 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 | -| §238 The acting rule's field `acts` with `step`, `on` and `bound`, the definition format staying 1 | the steps and the language match are what the indexer's map and the checker's cases held in code (research 085, Findings 2); the reader refuses an unknown field, so the runtime learns it in the one rebuild the milestone makes | -| §239 The fingerprint handed as `MESH_FINGERPRINT` at a process's start | one value every runtime reads before anything else, a service and a container alike | -| §240 The delivery record's consumer, held and failed outcomes, end and `record-ended`, the finding `record-open`, node away and wake, switch done once, and tools | a restart must lose and double nothing; a reader sees why an entry stands still; a node asleep must not hold a delivery open; a switch run twice must ask no approved group again | -| §241 A small fix's own record | a small fix is a milestone of one issue (ADR 0493 §3) | -| §242 An Approve given held while its heads stand (issue 1972) | the operator approved those heads; a check of them asked again changes nothing they approved (ADR 0441 §1, ADR 0548 §8) | -| §243 The milestone events on a subject of their own | a waiter on one member's pull request otherwise wakes on every member's | -| §244 The issue tracker's link kinds to a decision and a research effort, and the knowledge base's link from a decision to its milestone | the trace from research to the running build is then links all the way, with no text match (research 085, Findings 7; ADR 0493 §4) | -| §245 The view's state read and asked again by mesh-sdk's `go/views` tools `_first_views` and `_first_view_again`, which gain the revision a part holds and what it waits for; the typed first view and the following of each half in the seats' client packages `meshcatalogclient` and `meshwantsclient`, built on `go/views`; the finding `view-waits` after five minutes; and `node_process_builds` | every part's view and every process's own build is read and steered by a tool; `go/views` already follows an owner's events durably and serves those two tools, so a second follower or a second pair of tools would be two forms of one thing; a view tool typed by the two seats would make mesh-sdk import contracts (ADR 0635 §4), so the typed half sits in the seats' client packages (ADR 0625 §4); a part waiting for the view is raised on the bound the seat list's readers keep | -| §246 The seat list's reader and its two tools wired once by mesh-sdk's runtime | ADR 0636 §7 puts them in the runtime bundle; four hand-wired copies would each learn `acts` apart | -| §247 The trunk's heads from the catalog half, and nothing read of the gate by the checker | ADR 0596 §2; with the hold and turns removed nothing of the gate is left for the checker to read | -| §248 The indexer reads every contracts trunk's definitions at its start, before it judges | a definition merged while it was down is never judged against a list that lacks it (ADR 0636 §1) | -| §249 Another node's running build kept from the node-engine seat's `running` events by a durable consumer, the event carrying the fingerprint and the manifest's digest, the trunk's manifest for a node that said nothing | ADR 0660 §2 reads the manifest of the build each node runs; an event kept by a durable consumer answers while a node is away, where a call would wait on it; one fallback in the one function keeps the two parts agreed (ADR 0660 §3) | -| §250 What the gate and the checker owe the forge said as the event `owed` per repository and pull request, each act safe to repeat at the forge, each outcome said as `owed.ended` and recorded, an unanswered act said again at the gate's start and stalled after 15 minutes | ADR 0596 §1, §2 end the watches of the gate's and the checker's keys; an act safe to repeat cannot be lost between the act and its record, where an id recorded first could; an outcome said and recorded leaves no act unseen | -| §251 A merge made at the forge opens a `forge-made` delivery record | its trace and outcomes are kept like every delivery's, and no later merge waits behind a merge the gate never heard | -| §252 Every delivery part and the gate reach the forge only through the seat `git`'s verbs and events, the act verbs granted by the holder's trusted `callers` to the forge bridge alone, a failed read answered as an error | the delivery parts name no other module (ADR 0656 §1, acceptance test 27); the forge's answers are typed in the seat's contract instead of guessed in each caller; the bridge stays the one part that acts on the forge; an error read as an empty answer would hide a forge down | -| §253 A check that failed because a part of the mesh was not heard says `could not run`, naming the part, releases its claim, and is asked again once per heads and part on that part's `running` event, raising `check-could-not-run` after 60 minutes | the operator sees a mesh fault apart from a failing change, and the check is asked again on the one event that can change its outcome, never on a timer (issue 1373) | -| §254 A module held for its claim raises `module-held` until it is composed | a module left out in silence would look delivered (the mesh's rule that failure is loud) | -| §255 The indexer waits for the seat `git` at most 2 minutes at its start, then judges from its kept definitions and raises `view-waits` after five | longer than a forge's restart, and no start held behind a forge down for good | -| §256 The forge before the forge bridge before the gate, reverted the other way | the verbs are served before they are called, and `owed` is said only to a bridge that hears it | -| §257 The store seat's holder's own new build kept on its build node and fetched by `kept-artifact` when its seat is broken | its own build needs the store it replaces; a step of the delivery replaces the hand bootstrap | -| §258 Every drop and release tool writes the hand-act log | a hand act that changes what the mesh runs or keeps is recorded with who did it and why, as every hand act is (seat `mesh-hand-acts`) | - -§259 Each choice is checked by the tests its section names; the review by architectural domain judges them. - -### What each step says - -§260 Each step says what it did as its own event, the event of the part that did it. A catalog graph revision kept -carries the source revision; an assignment graph revision kept and a difference worked out carry the source revision -and the assignment graph revisions; an entry taken in, built, checked, composed, wanted and running also carries the -entry's fingerprint and its node. The gate's delivery record is built from them. The seat's event comes first where -the parts follow it through a seat (ADR 0656); a holder's own event stays under its own subject, declared in its own -manifest's `streams` and published on `mesh.stream..event.` (ADR 0363 §1, §2). A seat's event is -declared in its definition and its holder's manifest claims the seat; a seat event's name has no dot, since it is read -as a subject's token, and it is kept on the stream `SEAT__`, which the grants part makes with its durable -consumers, keeping it by the age and cap the definition's `keep` names, 90 days unless it names less. **An event a node publishes carries the node in -its subject**, and the grants part grants each node-engine's account only its own node's subjects, so one node cannot -say another runs a build (ADR 0363 §2); the gate's events are granted to the gate's account alone. - -| Step | Event | Owner: the seat, then the module | Subject | Declared in | -|---|---|---|---|---| -| a catalog graph revision kept, saying whether it is a trunk revision or a candidate's and its watched branch | `catalog-revision` | `mesh-catalog`, the indexer | `mesh.seat.mesh-catalog.event.catalog-revision`, the watched branch in the subject from G's delivery | the seat's definition in `mesh-delivery-contracts` | -| an assignment graph revision kept | `assignment-revision` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.assignment-revision` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | -| a difference worked out | `difference` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.difference` | the same | -| an entry taken in | `take-in.taken`, which gains the entries | the indexer's own | `mesh.stream.mesh-delivery-indexer.event.take-in.taken` | the indexer's manifest | -| an entry built | `build.ended` | the builder's own | `mesh.stream.mesh-delivery-builder.event.build.ended` | the builder's manifest | -| an entry checked | `checked` | `mesh-delivery-checker`, the checker | `mesh.seat.mesh-delivery-checker.event.checked` | the seat's definition in `mesh-delivery-contracts` | -| an entry wanted | `wanted` | `mesh-wants`, the assigner | `mesh.seat.mesh-wants.event.wanted` | the seat's definition in `mesh-delivery-contracts` (ADR 0641 §2) | -| an entry composed, and running healthy | `composed`, `running` | `node-engine`, the node-engine on each node | `mesh.seat.node-engine.event.composed.`, `.running..`, the latter on a stream kept last per subject with no age limit | 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.` | the same | -| 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..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..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.` | the seat's definition in `mesh-delivery-contracts` | -| a milestone's ask asked, withdrawn and answered, until G's delivery | `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.`, the milestone in the event, on the seat's family stream, apart from each delivery's `transition` | the same | -| a delivery record opened, and a merge named a recovery | `record-opened`, `recovery-merged` | `mesh-delivery`, the gate | `mesh.seat.mesh-delivery.event.` | the same | -| a pipeline, its steps, its waits and a run on request, from G's delivery | `pipeline-started`, `pipeline-ended`, `group-ended`, `run-started`, `step-started`, `step-ended`, `wait-held`, `wait-ended` | `mesh-pipeline-runner`, the runner | `mesh.seat.mesh-pipeline-runner.event....` | the seat's definition in `mesh-delivery-contracts` | -| a pipeline step ended, from G's delivery | `step-ended` | each build seat, `mesh-wants` | `mesh.seat..event.step-ended.`, the node that ran it | each seat's definition | -| a registration changed; a package published, from G's delivery | `registration-changed`; `package-published` | `mesh-catalog`, the indexer; `mesh-builds`, the builder | `mesh.seat.mesh-catalog.event.registration-changed`; `mesh.seat.mesh-builds.event.package-published` | the seats' definitions in `mesh-delivery-contracts` | - -§261 The events, their fields, their subjects and which are a seat's are this design's choices: a seat's event where -other parts follow the step through the seat, a holder's own event where only the delivery record hears it, so that -nothing reads another module's bus state (ADR 0596). The builder's waits go on its own stream because the builder -holds no seat of its own; the watchman and the gate follow them there. How it is checked: each seat definition's -conformance fixtures name the subject of every event it gains, and a case of the holder's whole-module test matches an -event it publishes against them, as for every seat (design 26); the gate's whole-module test hears each row through -its durable consumer, with exactly the fields this section gives it; a `running` event published by one node's account -on another node's subject is refused by the bus. - -### Their tools - -| 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), `assigner_puts` (each put with its step, since when, held or not, the `running` that ended it, and for a put without hold whether it is past its 30-minute bound; a put older than the trunk's newest build of its module said so) | `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` | - -§262 Every drop and release tool writes its act, who did it and why, in the hand-act log of the seat `mesh-hand-acts`. -Beside ADR 0636 §7's two tools in mesh-sdk's runtime bundle, which read a reader's seat list and make it read the -list again, the view is read and steered by the two tools mesh-sdk's `go/views` already serves every part that takes a -first view: `_first_views` gains which view revision the part holds, what it waits for and since when, beside -what it heard and how far it is behind, and `_first_view_again`, granted as ADR 0636 §7's second is, makes it -read the view again now. `go/views` stays seat-neutral, naming no seat and no seat's type (ADR 0635 §4); the typed -first view of each half and the following of its events are the seats' client packages, `meshcatalogclient` for -`mesh-catalog` and `meshwantsclient` for `mesh-wants`, each built on `go/views` (ADR 0625 §4), so no part follows the -view by a follower of its own. A tool -of the node-engine, `node_process_builds`, reads the build each process was started with. The tools answer the four -lines every module's tools answer (design 26), so that every state these parts keep is read and every act they do is -steered by a tool. - -§263 **How it is checked.** Unit (assigner): a revision of the assignment graph computed again from its named inputs -equals the one kept; a fixture change with a new fingerprint and an empty difference is refused, naming its module; -one revision of about 120 modules on four nodes stays under the bus's value limit; of the view the assigner reads only -the catalog half. Unit (indexer): a catalog graph revision holds one entry per module of its source revision. Whole -(indexer): a source revision taken in gives one catalog graph revision kept as objects in an `objects` section in the -old form within the cap its manifest declares; the manifest validator refuses the indexer's or the assigner's manifest -that declares them in the new form. Whole (indexer, assigner): a revision a delivery record pins is not removed; one -two records pin stays until both free it; one neither pinned nor current is removed; a `freed` said while the part -was stopped is applied after its restart; a candidate's graphs are dropped on `heads-replaced`; a store past 80 % says -`filling` and raises `store-filling`, and a full one says `full` and refuses its next revision naming itself; for a -candidate's heads both `revision` verbs answer, the assigner's half named by its own revision and naming its inputs; a -placement change with no merge, and a `capabilities-changed`, each give a new assignment half on the same catalog half, -whose difference moves only the module concerned, and a part takes that pair as its view; after its first view a part -acts on a seat event without asking a verb again; a running part does not act on a candidate's view; a pull request -that adds a seat and a module using it is validated by the indexer's candidate pass and checked by the checker with -the same definitions; the tools of the table answer and the drop tools write the hand-act log. Whole (assigner): a -pull request's graph leaves every node's want unchanged; a placement-only revision moves the want of the module it -places; the builder and the grants part read their entries only through the seat `mesh-wants`'s verbs and events, and -the node-engine reads its want and the catalog half and never the assignment half. Whole (node-engine): a part started -while its node's want differs from what runs reports the build it was started with. Live: a search of the readers' -code finds none of the reads the revision view replaces, each by name: the indexer's read of the checker's previews, -the checker's watch of the indexer's `candidates.`, the forge bridge's read of the gate's `merges` and `owed`, -the checker's read of the gate's `source` and `turns`, the forge module's watch of the checker's `owed` (its `mesh/check` -status, now the checker's `owed` event performed by the forge bridge) and its read of the gate's `source`, the grants -part's read of the assigner's state `wants`, any module's read of the indexer's `modules` (its tool -`indexer_read_modules` in an `invokes`, its view in a `consumes`), the -checker's validator request with no seat definitions, a seat list compiled into the node-engine, the checker, the -builder or the grants part, a seat list's reader wired by a part itself, and a call of the forge module's own tools by -a delivery part or the gate; an old node-engine that wakes takes its new build in a form it reads. - -## Order without a person: the acting parts - -### An acting part's new version acts first, a wait with a bound - -*ADR 0656, which supersedes ADR 0646; ADR 0603 §1 to §4, §8, §11, ADR 0614 §1, §3, §6, ADR 0633 -§1, ADR 0539 §7, ADR 0584 §1, §2, ADR 0564 §2.* - -§264 From research 085's milestone. A seat's definition may carry a rule, the step its holder acts in and the modules it -acts on, and a holder of such a seat is an **acting part** (ADR 0656): the indexer taking in and validating, the -checker checking, the build modules building, the node-engine composing, the grants part granting, the assigner -placing. *What ADR 0656 leaves to this design*, above, gives the rule's fields. Adding one needs its manifest and -its seat's rule, and no code elsewhere: the indexer's map from language to build module and the checker's own module -names go, since every new acting part needed code changes elsewhere (research 085, Findings 2). - -- §265 **The rule becomes edges between versions.** In the assignment graph the assigner gives an edge from the acting - part's version to each version its rule names, and no part names another module in its code to order by (ADR 0656). -- §266 **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. 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; from G's delivery 30 for the runner's `pipeline`, whose wait ends when the new runner runs healthy, one module's apply on one node as composing's is (the design's choice under ADR 0666 §19, 2026-11-10). 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). -- §267 **A build module's wait ends on the agreed toolchain.** For building, an entry waits until the agreed toolchain of - its language (ADR 0584 §1) names the new build module, never merely until the new build module runs on one node: a - fingerprint names the agreed toolchain (ADR 0603 §3), so only then is the module built once, by the new one. A build - 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. -- §268 **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..` 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. -- §269 **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 - 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 - 4; stopping the whole delivery would hold modules the failed part does not act on (the operator, 2026-11-06 17:0x). -- §270 **A wait holds only within its step, and skips the holder's own inputs** (ADR 0656). An entry waits only for its - own step's holders, so two acting parts that act on each other in different steps (the indexer taking in the - checker, the checker checking the indexer) form no cycle. Within a step a holder's own change is its first act, and - what its new version is built from or needs (a base, a package, a provider) is acted on by its running version, - never waited for: the running build module builds the shared contracts and the registry holder, then the new build - module builds the rest, so ADR 0614 §6's cycle through them stays allowed, now for that reason. -- §271 **The artifact store's holder takes its own new build even when its seat is the one broken.** A build module - stores the artifacts it builds through the seat `oci-artifact-registry`, so the - holder of that seat is in its own build's path: its new build needs the store it replaces. As every holder's own - inputs are (above), that store is acted on by the running holder: the running holder stores its new build, and only - then is the new one wanted. When the running holder is what is broken (the seat refuses or does not answer a store, - as in the hand bootstrap of 2026-11-07 to 11-09), the build module keeps that one build on its own node among its - kept builds (*The bootstrap delivery*: `build_go_kept`), says in its step key that it is kept on that node, and the - builder's done key names it so with its digest; the node-engine composing the holder fetches it through the build - seat's verb `kept-artifact` of that node, and the new holder, once it runs, takes the store of every build after it. - - The verb sits in the build seats' definitions (`node-build-go`, `node-build-node`, `node-build-image`, in - `mesh-delivery-contracts`), and its callers are the node-engines alone: the definition names them, and the grants - part grants it to no other account. - - It answers only for a module that claims the seat `oci-artifact-registry`, and refuses every other, naming the - module, so a store outage never turns into delivering from build nodes. - - The bytes go as the reply's object in the build module's sent store `_sent-objects` (*Any size*), never in - a value, since an artifact is over the bus's value limit; the build module is no foundation part, so it keeps no - old `objects` section, its kept data being in ADR 0595's templates (ADR 0666 §4), and the build itself stays among - its kept builds on its node (the design's choice, 2026-11-10, correcting the old form this line named); the node-engine checks the bytes it fetched against the digest in the - builder's done key and applies nothing that differs. - - While the keeping node is away, or its sweep removed the build, the want waits, and the node-engine says in its - have that it waits for that node or for a build no longer kept. - - This is the one artifact the mesh delivers from a build node instead of the store, and only for the holder of - `oci-artifact-registry`; the design's choice under ADR 0656 §3 (a wait skips what the holder's own new version needs) and ADR - 0526 §2, whose build by a session's call it makes a step of the delivery instead of a hand act. How it is checked: - test 60. -- §272 **The check refuses a cycle among the waits within one step** (ADR 0656, which changes ADR 0603 §2's refusal of a - cycle), counted beside the edges ADR 0614 §3 and §6 list; a cycle through an acting part's edges across two steps, or - through a holder's own inputs, is none. -- §273 **The indexer's own steps have no wait, and a newer indexer is reached.** Taking in and validating stay as *Taken in - 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 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. -- §274 **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. -- §275 **An acting rule grants nothing.** A holder's bus grants come from the grants part as today (ADR 0539 §7). An - acting rule changes what a part acts on across every module it matches, so the operator's Approve ask of a change - names any acting rule it adds, and any it widens: a `step`, an `on` or a longer `bound` that now matches more - modules or holds them longer is named like an added rule. From G's delivery there is no Approve ask per merge, and - the pipeline's end names it in `widens` instead, refusing nothing (ADR 0666 §23). -- §276 **Each step's wait is its own part's, read and released by its own tools.** The part that does a step holds its - wait, so each wait has one owner that already holds that step's state, and says `wait-held` and `wait-ended` (*What - each step says*) with the step, the acting part, its version, the entry and since when: - - | Step | Holder | Reads its waits | Releases one | - |---|---|---|---| - | 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 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. -- §277 **How it is checked.** Whole (assigner): a fixture seat declaring a rule, with a fixture holder, gets its edges with - no code change elsewhere. Whole (assigner, builder, three build nodes): a group changing the shared contracts and the - build module that builds them, the new build module updated on the first build node first, builds no module with the - 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, - 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 (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 - through a build module's edge each pass the cycle check, each new version first in its own step; a cycle among - waits within one step is refused. Unit (assigner): the Approve ask of a change that adds an acting rule, and of one - that widens one, names the rule. Live: a search of the delivery parts' code finds no other module's name. - -## Nothing merges into a repository under check: removed - -§278 **Removed at research 085's milestone.** The hold, the merge turns, their stalled line, the gate's tools for them and -the setting `check-gates-merge` go in milestone G, the gate's and the forge module's alike (the operator, on Telegram, -2026-11-09 01:11 UTC, choosing "Remove them in G"; the forge's merge then judges no check, *The seat `git`*). The setting has been false since 2026-10-25, so the gate wrote no turn and held no merge in that time, and the -operator's word for G is that no check gates delivery (1921's note of 2026-11-07). From G's delivery no merge waits on a check, and none on an Approve either (ADR 0666 §10): the checker is gone, and the -pipeline runs on the landed commit (*Pipelines per module*). **Its record is accepted:** ADR 0661 ends ADR 0548 §2 -to §4, §9 and §10 and ADR 0551 and changes ADR 0534 §4's judgement of `mesh/check` by the merge; until G's delivery the -rule below holds as those records have it. ADR 0662 is withdrawn; G builds ADR 0661 §1, §2 and §6, and ADR 0666 §10 -changes its §3 to §5 (the merge on the Approve alone), which G therefore builds for no merge. Beside it, ADR 0663 (accepted: the indexer answers `revision` in its own runtime to -exactly the parts that invoke it, the tool runner among them) holds the tool -runner's first view of the catalog half (*Every other reader of which modules exist*). How it is checked: in the gate's whole-module test an approved merge into a -repository under a running check is handed at once, and the gate's manifest declares no `turns` and no setting -`check-gates-merge`; in the checker's, a check claims at once with no gate state granted to read. - -## Checks: the check on the change's own acting parts - -### The check on the change's own acting parts - -*ADR 0656, which supersedes ADR 0648, ADR 0644; ADR 0448 §2, §4, ADR 0391 §1, ADR 0603 §10, ADR 0614 §7, ADR 0633 -§6, ADR 0636 §4, ADR 0639 §5, ADR 0498 §1.* - -§279 From research 085's milestone. ADR 0603 §10 and ADR 0614 §7 let a check pass while the part that would build or -check the change never ran: 7 of 24 failures since 2026-11-01 (research 085, Findings 4). The acting parts a change -alters, and the checking program, the rules every check applies, are built from the change's heads and check it; the -running versions never check in their place, and a check fails, naming the part, when one cannot be built or run -(ADR 0656). - -- §280 **Two tiers on the change's assignment graph.** The first tier builds, at the change's heads, only the acting parts - whose fingerprint in the change's assignment graph differs from the running one and which are to act on a module - the check acts on; a change to an ordinary module asks no first-tier build, since building every acting part for - every check would make an ordinary change pay for builds that change nothing. The second tier runs the check's - levels with those programs and with the revision view of the change's heads (ADR 0656). This keeps ADR 0603 §10's - building of changed build modules first and widens it to every acting part. -- §281 **The judging rules are a checking program built from the change.** Every check builds its checking program from - the change's heads and runs it with the revision view as its input; when it cannot be built or run, the check - fails naming it, and the running checker's own rules never judge in its place (ADR 0656). It is built once per - fingerprint and reused (ADR 0498 §1): checks whose heads leave the checking program unchanged ask one build between - them. -- §282 **Every program built from a change runs only in the build seat's sandbox.** The acting parts the first tier builds - and the checking program run in the run step, with no network (ADR 0448 §2) and no bus account, the logins staying - outside (ADR 0448 §4), through the seat `node-build-go`'s ask `run` (*What ADR 0656 leaves to this design*), as - the validator, the composer and the Go build module already run; a change not yet merged is not trusted. The running - checker alone asks, reads the program's verdict, records it and publishes it. -- §283 **A first tier that cannot build an acting part fails the check, naming it.** The verdict never passes on the - running version's word: a module whose changed build module the check cannot run is no longer composed from its - manifest (ADR 0656, which changes ADR 0603 §10 and ADR 0614 §7), and ADR 0614 §7's reason, a changed build module - 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`. -- §284 **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. -- §285 **ADR 0633 §6 stays, beside the change's own validator.** Whether the running parts can take the change in is - still checked with the trunk's manifest validator, built from the trunk (ADR 0633 §6); the change's own validator, - built in the first tier, judges the change's manifests besides (ADR 0656). A change fails if either refuses it. -- §286 **The asking and recording are covered by the checker's whole-module test**, which starts the changed checker on a - real bus and asks one check of a fixture group end to end (ADR 0391 §1). -- §287 **One seat list for both readers of a change's heads.** Inside every check, a lone pull request's as a group of one, - the checking program built from the heads makes one seat list from them, and the checker hands it to the manifest - validator and the composer (ADR 0644, ADR 0636 §4 with its insight of 2026-11-06); the running checker reads no - definition of the heads itself (*The bootstrap delivery, and the one milestone after it*). -- §288 **Its tools.** The checker's read of a check names the checking program's build that judged it, its fingerprint and - the heads it was built from, and the rules named changed; `checker_first_tier` lists a check's first-tier builds, - each with its acting part, its fingerprint and its outcome (`built`, `waiting for a build node`, `did not build`), - and asks one of them again. Asking a whole check again stays the checker's existing ask-again. -- §289 **What it costs.** A check of a change to an acting part costs one build per changed acting part, within the - checker's step bound (ADR 0639 §5); an ordinary change costs nothing more; a checking program costs one build per - fingerprint, shared by every check that uses it. -- §290 **How it is checked.** In the checker's whole-module integration test: a fixture change whose acting part does not - build, and one whose checking program does not run, each fail their check, naming the part; a checking program that - 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 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 - same checking program ask one build of it; a group that changes the assigner's placing is checked with its own - assigner; a fixture indexer change whose manifest the trunk's validator refuses fails its check, even when its own - validator passes it; a fixture group whose heads add a seat has that seat in the list both the validator and the - composer receive; the check's read names the checking program's build, heads and changed rules; `checker_first_tier` - lists a check's first-tier builds and asks one again; the changed checker asks one check of a fixture group end to - end. Live: the registry group passes its one check with its own checker, validator and composer. - -## After the merge, from milestone G - -- §291 **From milestone G's delivery, per watched branch.** The gate writes one source revision per watched branch, - `source.`, as a commit or a group lands there; the pipeline runs on each revision the indexer takes in, and - the wants follow a revision whose `required` steps passed, on the branch each placement chose (ADR 0666 §6, §9). -- §292 **From research 085's milestone, the wants follow the trunk's assignment graph.** A merge taken in makes a - catalog graph revision and then an assignment graph revision; the assigner writes each want from that revision, - its rules unchanged (ADR 0656), and each acting part's new version acts first in its own step (ADR 0656; *Two - graphs, one source of truth*, above). - -## Taken in by the indexer it names, from milestone G's delivery - -- §293 **From milestone G's delivery** (ADR 0666 §6, §13, which change ADR 0633 §1 and §3 to §6): before any take-in, on every - watched branch, the running indexer validates the revision with the manifest validator built from its heads, in the - sandbox, and a revision it refuses stays pending, as the bullets above have it, with the way back a revert; a pending - revision holds only its own watched branch; a group's member reaches it by landing, and the group ends when the gate's - group verb says every member has; `bridge_group_end_again` reads that verb; and the check's refusal below goes with the - check (*Watched branches and the catalog graph per branch*, *Groups*). - -## A backup before the apply: kept data on the bus - -### A module's kept data on the bus, backed up and restored with its delivery - -*ADR 0404, ADR 0235, ADR 0233, ADR 0539 §16, ADR 0595, ADR 0619 §2, ADR 0633 §1, §5, ADR 0363 §2; from research -085's milestone, this design's choice under ADR 0656.* - -§294 **ADR 0404's backup does not cover a module's kept data on the bus.** It backs up the items a moved module keeps on -its own node (ADR 0404 §2, ADR 0233), and the bus's streams are dumped whole by the bus module on its own rhythm -(ADR 0235), not per module and not before a delivery. A key-value state, a stream or an object store a module keeps on -the bus (ADR 0595, ADR 0619 §2), and its durable consumers' positions on the streams it reads, therefore have no -restore point taken before its module moves. That is a gap of the delivery system, and milestone G builds it, because -a revert of milestone G is the first change that needs it. It is built as the delivery system's capability for every -later change of a kept data's form, not as a case of this milestone. - -- §295 **Its form is declared.** Each kept data a manifest declares names a `form`, a number its holder raises whenever it - changes how it writes that data, or stops declaring it; a declaration naming none reads as form 0. The declaration - alone cannot tell a changed layout of the same bucket, so the holder says it. Only kept data whose `form` changes is - backed up and restored, so a part's other kept data, and every decision it holds, is never set back. -- §296 **It is backed up before the module moves.** When a want moves a module whose new manifest declares a kept data at - another `form` than the running one, or no longer declares it, the node agent applying it first asks the seat - `node-backup` of the node holding `mesh-broker` for a restore point of that kept data, and waits for its event - `ended`, as for any backup before the apply. The bus module's account takes it, as it takes the bus's snapshots - (ADR 0235 §5). A restore point is keyed by the module, the kept data, its form and the fingerprint it was taken - before, and it holds the kept data whole, the position of each durable consumer the module reads with, and the - catch-up tool the newer manifest declares beside that kept data. A failed - backup holds that module as any failed backup does. -- §297 **A revert restores it before the reverted build starts.** When a want takes a module back across a change of a - kept data's `form`, the node agent applying it asks that seat's verb `restore` for the restore point of that module, - kept data and form, waits for the seat's event `restored`, and only then starts the reverted build. The node agent - that restores is the one running when the revert starts. As every restore is, it is brought back beside the live data - and never over it: the live kept data of the newer form is set aside under a name of its own, and only the operator - removes it, so nothing written since is destroyed. The consumer's position is set back with it, so the reverted - build hears everything since as after any restart. -- §298 **The order of a delivery that crosses a form change** is the module graph's (*Order without a person*), with three - edges the assigner, which writes every want, keeps: - - the new versions of the `node-backup` holder and of the bus module run before any module whose kept data's form - changes is moved, since the restore point needs both; - - at a revert, a node's node-engine goes back last on that node, after every module it restores there has said - `restored` and runs, since the node-engine of before may not know how to restore; - - the reverted assigner and gate are wanted only once the reverted indexer has taken in the trunk's head, as its - `take-in.taken` naming the current source revision says (ADR 0633 §1), since the assigner of before computes the - wants from the indexer's `modules` and would otherwise write the fingerprints of the restore point. Until then the - running assigner writes no want, and every module keeps the fingerprint it runs. -- §299 **The restored part catches up forward, with the code it already has.** Read on main, mesh-delivery at 2d201f78 - and mesh-catalog at 1d07778c: - - **The indexer** (mesh-delivery, `modules/mesh-delivery-indexer`): its durable consumer `_` on the stream - `forge` redelivers every message since the restored position, and `handle` takes each - merge through `Indexer.Arrive` into `pending` and then into `modules` (`applyMerged`); `applySource` takes the - gate's `source.main` in, and `ApplySource` leaves a revision older than the latest it has seen. **The stream - `forge` keeps 7 days**: the forge bridge's manifest declares no age, and a stream's default is 7 days, at most 30 - (mesh-sdk `go/catalogue` `StreamDefaultDays`, `StreamMostDays`), so it does not cover the records' 90 days. A - merge older than the stream's first message is never redelivered, and the code then leaves that repository's - modules at the restore point's fingerprints. So the restore runs one forward step: once the reverted indexer - runs, the node agent that restored it calls its tool `indexer_take_in` with each repository the gate's source names - (`NewTakeInRepository`, which takes in the head the source names for a repository). The catch-up tool is declared - by the newer build's manifest, beside the kept data whose form it changes, and recorded in the restore point when - the backup is taken; the node agent takes it from the restore point, never from the manifest being applied, since - at a revert that is the manifest from before the change, which names none. So the delivery system names no - module's tool. - - **The assigner** (mesh-delivery, `modules/mesh-delivery-assigner`): it keeps nothing restored. Its pass - (`passer.wants`) writes, from what the world holds now, each node's want by the six rules from the indexer's - newest fingerprint at the trunk, and only where the value differs; so once the indexer has caught up it writes - the trunk's wants, and nothing older. - - **The gate** (mesh-catalog, `modules/mesh-delivery-gate`): `Holder.Load` reads the restored deliveries and groups - back; `readOnStart` then reads what happened while it was away: `wantForge` reads the forge's open pull requests - at the next look, `hearAsks` reads the teller's answer to every ask it still holds as pending, and - `SourceCatchUp` brings the newest merge of each repository into `source.main`, never moving a repository back. An - ask the gate of milestone G raised after the restore point is not among the pending ones, so its answer is not read - and the operator is asked again. Whether the first look clears a merge kept in the restored `merges` that the forge - merged since was not read in the code; the catch-up test below proves it with the real builds. -- §300 **A missing restore point holds the revert, naming it.** When the restore point a revert needs does not exist, or - `restored` does not come, the node agent applies nothing of that module, keeps what runs, and writes the have phase - `held` with the reason `restore point missing` and the module, kept data and form it names; the assigner asks the - operator (hold, or start the reverted build on the live data as it is), raises the condition naming them, and on - the answer writes the node's want again naming it (ADR 0539 §16). -- §301 **What the operator is told.** A revert across a change of a kept data's form puts that kept data back to the - moment before the change moved it: the deliveries made since stay delivered, but what the part wrote there since - (for milestone G, its delivery records and its graph revisions) is set aside and not read again; an Approve asked - and answered since is asked again, and one the gate asked before still reads its answer. The revert's Approve ask - says this in those words, naming each kept data restored. -- §302 **Its tools.** The seat `node-backup`'s `backed-up` lists each module's restore points of its bus data, with the - kept data, the form and the fingerprint each was taken before, and the data set aside at each restore, with its - size; `restore` restores one; `discard-aside` removes data set aside by a restore, only on the operator's call, and - writes the act, who and why in the hand-act log of the seat `mesh-hand-acts`. `backed-up` and `restore` exist on - the seat today and gain the module's kept data on the bus as an item; `discard-aside` is new. -- §303 **How it is checked.** Whole (node-engine, backup holder, bus module): a want moving a module across a `form` - change, and one moving a module whose kept data declares no `form` to one that does, each take a restore point - first, keyed by module, kept data, form and fingerprint; a want taking it back restores it beside the live data and - the consumer's position before the reverted build starts, and the data set aside is listed with its size and - removed only by `discard-aside`, which writes the hand-act log; a revert of a group holding the node-engine and a - module whose form changes restores the module before the node-engine goes back; a revert whose restore point is - missing is held, naming it, and moves on only on the operator's answer. The catch-up is proved with the real builds - of the indexer, the assigner and the gate from before milestone G, never fixtures (acceptance test 36). - -## The verbs milestone G adds - -§304 From research 085's milestone the seat gains: - -| Verb or event | Answers or does | -|---|---| -| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656), and the acts the gate said it owes the forge for it, each with its id and its recorded outcome (*What the gate owes the forge*) | -| `owed` | an act the gate owes the forge (a merge, a status, the delivery's view, a comment, a line on the commit's note), with its pull request, head and id, on `mesh.seat.mesh-delivery.event.owed..`, said only from the gate's account and heard by the forge bridge through a durable consumer (*What the gate owes the forge*) | -| `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | until G's delivery: the one Approve ask per milestone (ADR 0493 §9 as ADR 0659 §1 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.`, the milestone in the 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`. G's delivery ends the ask and its three events with the Approve per merge (ADR 0666 §10) | -| `record-opened` | a delivery record opened, which the watchman times for `record-open` | -| `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`, `room` | a delivery record ended; the ledger past 80 % of its cap; the ledger full; the ledger back below its cap (*The delivery record*) | - -§305 From milestone G's delivery the seat's `table`, `recheck` and `close` go with the merge states (*The state table*), -the milestone ask and its three events with the Approve per merge, and it gains (ADR 0666 §6, §26): - -| Verb or event | Answers or does | -|---|---| -| `source` with a watched branch | that branch's source revision; the indexer and the node-engine's catch-up read it, never the gate's state | -| `source-moved` | a new source revision of a watched branch | -| `recovery` | marks a merge its author names a recovery (the commit and why), its record marked `recovery` with the caller the runtime proves | -| `record-opened`, `recovery-merged` | a delivery record opened; a merge named a recovery, which the teller passes on to every channel | - -§306 **The seat `mesh-pipeline-runner`**, from milestone G's delivery, defined in `mesh-delivery-contracts` with the callers -of each verb (ADR 0666 §7, §20, §25): - -| Verb | Answers or does | Callers | -|---|---|---| -| `run` | the run on request of one or more members, each a module at a named commit; delivers nothing | the sessions (the agents' bus identities the tool runner proves) and the operator, nothing else | -| `start` | a delivering pipeline for a watched branch's head, by hand | the operator, the healers | -| `show`, `list`, `stands`, `why-carried`, `kept` | one pipeline whole with its step log; the pipelines and runs of a module, a branch or a commit; what stands still; where a result was carried from; the kept results | any person, an agent session, the healers | -| `review` | a code review's result for a commit and fingerprint | the reviewers the definition names; refused from the commit's author | -| `rerun`, `accept`, `cancel`, `ask-again` | one step again; a failed `optional` step accepted with why; a pipeline ended, its asks dropped; a declined `approval` asked again | the operator, the healers (`accept` and `ask-again` the operator) | - -§307 **The seat `mesh-catalog`** gains, from milestone G's delivery, `register`, `policy`, `watch`, `unwatch` and -`registrations`, and the event `registration-changed` (*A module's registration*), and `catalog-revision` names its -watched branch in its subject. **The build seats** gain the verb -`steps` and the accepted kind `step`, and **the seat `mesh-wants`** the kind `step`, each saying `step-ended`. **The -seat `mesh-builds`**, new and held once for the mesh, says `package-published` (*A publish rebuilds*). - -§308 **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 -each step says*. - -§309 **The seat `node-engine`**'s event `running..` carries, from research 085's milestone, the module, its -fingerprint and the digest of its build's manifest, so the node-engine of another node and the grants part decide a -seat's pass from it (ADR 0660 §2; *Who holds a seat is applied by one rule*). No verb is added. - -§310 **The seat `git`.** The seat `git` (design 26, *The git seat*), of the Delivery domain, defined in -`mesh-delivery-contracts`, holds no verb today, and the delivery parts and the gate reach the forge by naming the forge -module's own tools and events and guessing the shape of their answers (on 2026-11-09 one guess, an answer wrapped in a -`result` field, made 316 conditions stand, issue 1966). From research 085's milestone the seat's definition carries the -verbs and events below, their inputs and answers typed in its contract, served and said by the module that claims the -seat; every delivery part and the gate call and hear the seat, never the forge module by name (acceptance test 27). -They are this design's choice under ADR 0596 §1, §2 (a module's events are subscribed to and its data asked through -its seat verbs) and ADR 0625 §1 (a seat's contract is generated from its definition). The table is built from what the -parts' manifests declare in `invokes` and `consumes` and what their code calls, read on main on 2026-11-09 (mesh-delivery -042b8d1f, mesh-catalog's gate); a declaration nothing calls (the checker's `gitea_commits`, `gitea_compare`, -`gitea_list_branches`) gets no verb and is dropped from its manifest: - -| Verb | Answers or does | Called by (today's call) | -|---|---|---| -| `pull` | one pull request, by its repository and number or by its merge commit: number, base, head branch, head, merge commit, author, state, and `merging` while the forge merges it; an unknown one answers the error kind `not-found` | the forge bridge (a merge the gate did not make; `gitea_api`), the gate (a stalled line's author) | -| `branch` | from milestone G's delivery: a branch's head, and whether a protection rule refuses a direct push and admits only merges | the indexer (`register`, `watch`), the forge bridge (a watched branch's head) | -| `open-pulls` | every open pull request of every repository, its head and base, the statuses its base requires and those its head has | the forge bridge at its start and each reconnect (ADR 0443 §5), the indexer, the gate (`gitea_open_pulls`) | -| `tags` | a repository's tags, paged, the page's bound said when more exist | the forge bridge, for the builder's floor (`gitea_api`) | -| `trees` | the trees of one commit | the forge bridge, the checker, the indexer (`gitea_commit_trees`) | -| `file` | one file at one commit | the forge bridge, the checker, the indexer (`gitea_get_file`) | -| `contains` | whether one commit contains another | the indexer, the gate (`gitea_contains`) | -| `statuses` | the statuses a commit carries | the gate (`gitea_commit_statuses`) | -| `status`, `comment`, `view`, `note` | a commit status, a comment, the delivery's one view comment, a line on the commit's note under `refs/notes/mesh-plan`; a comment and a note line carry the act's id and an id already held is skipped | the forge bridge alone, performing the gate's and the checker's `owed` (`gitea_commit_status`, `gitea_add_comment`, `gitea_delivery_view`, `gitea_note_append`) | -| `merge-pull` | until G's delivery: merges a pull request at the head the operator's Approve names; refuses a head that moved, a branch no rule guards, a head the Approve does not name and a merge git refuses; judges no check (below). From G's delivery: merges with the forge tools' account (ADR 0398 §2) at the head the caller names, judging that it is still the pull request's head and that the branch's protection refuses a direct push, the protection read again on every `branch-moved`, and refusing a head when any run at its build-input fingerprint failed a `required` step, judged from its own state `failed-steps` heard from the runner's `step-ended`, never by a call; a head with no run yet or a passed one merges, it never waits for a run and never asks an Approve (ADR 0666 §11). A pull request already merged at that head is answered performed | until G's delivery the forge bridge alone (`gitea_merge_pull_request`); from it any session the tool runner proves, and the operator | - -| Event of the seat | Says | Heard by (today's event) | -|---|---|---| -| `pull.updated`, `pull.merged`, `pull.closed` | a pull request's new head, its merge with the merge commit, its close | the forge bridge, the gate (`gitea.pull.*`) | -| `forge.pull`, `forge.status`, `forge.delete` | the forge's own webhook of a pull request, of a commit status, of a deleted branch | the gate (`forge.pull`, `forge.status`), the forge bridge (`forge.delete`) (`gitea.forge.*`) | -| `repo.writable` | a repository the forge can write again | the gate (`gitea.repo.writable`) | -| `branch-moved` | from milestone G's delivery: a branch's new head, by a merge or an admitted push, from the forge's push webhook | the forge bridge | - -§311 Each is said on `mesh.seat.git.event.`, declared in the seat's definition, and heard through the hearer's -`consumes` of the seat. - -- §312 **A failed read answers an error, never an empty answer.** When the forge does not answer, refuses or answers what - the contract cannot type, the verb answers an error naming why; an empty list or an absent file is answered only - when the forge said so. A caller never reads an error as "none". How it is checked: the seat's fixtures hold an - error answer for each verb, and each caller's unit table has a row for it (the forge bridge, the checker, the - indexer, the gate). -- §313 **Who may call an act.** The holder's trusted config value `callers`, which today names per verb who may call the - two package seats' verbs (design 51), gains a line per verb of the seat `git`: the act verbs name the forge bridge - alone, the reads name each part that declares them in `invokes`; the grants part grants by it, and the holder - refuses a caller it does not name. From G's delivery the callers per verb are the seat definition's field `callers`, - the grants part's one source (ADR 0666 §20): `status`, `comment`, `view` and `note` the forge bridge alone, - `merge-pull` the persons and agent sessions the tool runner proves, the reads the parts that declare them. The forge module's own acting tools (`gitea_merge_pull_request`, - `gitea_commit_status`, `gitea_delivery_view`, `gitea_note_append`, `gitea_add_comment`) retire with the milestone, - and so do its watch of the checker's state `owed` and its read of the gate's `source`, so the bridge stays the one - part that acts on the forge. How it is checked: test 51 reads the grants, not the code. -- §314 **`merge-pull` judges no check, and from G's delivery no Approve, only the runs at the head's fingerprint.** The operator's answer that no check gates delivery (Telegram, 2026-11-09 01:11 UTC, - "Remove them in G") covers the forge module's own setting `check-gates-merge` as well as the gate's: both are - removed, and `merge-pull` merges an approved head whatever its `mesh/check` says. The record that ends ADR 0551 and - ADR 0548 §2 to §4, §9 and §10 also changes ADR 0534 §4's judgement of `mesh/check` by the merge; it is to be accepted - before the push (*Nothing merges into a repository under check*). From G's delivery it judges no Approve either, its caller - is any session the tool runner proves, and it refuses a head when any run at its fingerprint failed a `required` step, - never waiting for one (ADR 0666 §11; *Approval is a step, and a session merges a pull request*). The reads the - gate would have needed to merge (the forge's word for a conflict, the base's required statuses, the merging account's - write access) are not added: no part of the mesh but `merge-pull` merges, and it answers git's own refusal of a merge - as refused. -- §315 **The order is the module graph's, and each step has its way back.** The verbs are a served-verb edge (ADR 0614): - the forge module's new build runs before the forge bridge's, and the forge bridge's before the gate's, since the - bridge calls the verbs and performs the gate's `owed`, and the gate says `owed` only to a bridge that hears it; all - three are ahead of the acting parts' steps, which read through the seat. While the new forge runs and the old bridge - still calls the retired acting tools, the old bridge's acts fail loudly and stay unperformed in the old gate's keys, - and the switch carries them (*What the gate owes the forge*). A revert goes back in the other order: the gate, then - the bridge, then the forge, each wanted only once the one after it in the forward order runs its reverted build, so - no part calls a verb nobody serves. ADR 0656 §3 stands beside it: the acting parts' new versions act first in their - own steps. Two things cover the gap while the forge is still on its old build and serves no verb of the seat: the - indexer starts on its kept definitions (below), and a check whose read through the seat `git` fails says `could not - run`, naming the forge, and is asked again on the forge module's `running` (*A check runs to its end*). -- §316 **A part that cannot read the forge keeps what it has, and says so.** The indexer, at its start, waits for the - seat `git` at most 2 minutes; with no answer it judges from the last definitions it kept, says in its - `_first_views` that it waits for `git` and since when, writes `view-waits` after five minutes, and reads the - definitions once the seat answers; it then judges again every manifest it judged from kept definitions. Two minutes is longer than a forge's restart, and short enough that a start is - not held behind a forge down for good. How it is checked: test 54. -- §317 **How it is checked.** The seat definition's conformance fixtures hold one answer and one error of each verb and one - of each event, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's, the - indexer's and the gate's unit fakes answer from the same fixtures; a call of an act verb from an account other than - the forge bridge's is refused (test 51); live, a search of the delivery parts' and the gate's code finds no name of - the forge module's tools or events. - -§318 **Links of the trace.** No part reads a review line (ADR 0659 §1 to §3); who may review and the review verb's grant -stay issue 1782's, outside milestone G, and that verb gates nothing (ADR 0659 §2). The trace's links are this -design's choices under ADR 0656, so that one call traces research to the running build -with no text match (research 085, Findings 7). The issue tracker gains link kinds from an issue to the decision and to the research effort -it carries, made by a tool call and never by text, as ADR 0493 §4 has every link made; the knowledge base gains a -typed link from a decision to the milestone of the issue tracker that builds it, made by a tool call, which joins the -link kinds of its link-fits rule (design 55), beside the `became` link a research effort gets at its graduation -(playbook 02). How it is checked: whole (gate), one ask per milestone, withdrawn by a new head; -a waiter on one member's pull request wakes only on that member's transition. Whole (issue tracker, knowledge base): -an issue linked to a decision and to a research effort reads both back; a decision linked to its milestone reads it -back. Live: research 085's milestone's record reads back research 085 to its decisions, its milestone, its issues and -its pull requests with no text match. - -§319 **A session hears that a record was accepted** (issue 595). The knowledge base already says, on its holder's own -stream, the event `reviewed` for every judgement of a record; from milestone G that event carries, when the operator's -Approve accepts a record, that it was accepted and by the operator, with the warrant, and the tool runner's account is -granted to hear it, so a session waiting on a record wakes on it. The design's choice under ADR 0596 §1 (a module's -events are subscribed to): a new event of the seat `knowledge-base` would be a second path for the same fact (lead-47, -2026-11-09). How it is checked: in the knowledge base's whole-module test an Approve that accepts a record says -`reviewed` with what was accepted and by whom, and the tool runner's account hears it. - -## What retires at milestone G - -- §320 At research 085's milestone's delivery: the indexer's map from language to build module, and the checker's own - module names and its cases for a change to the manifest validator and to the Go build module (ADR 0656); a build - module's edge as a trigger, with the extra build per recipe change (ADR 0603 §3, as ADR 0656 changes it); composing - from manifests a module whose changed build module the check cannot run (ADR 0603 §10 and ADR 0614 §7, as ADR 0656 - changes them); the module graph's one key per module with five past values as what parts read (ADR 0603 §2, as - ADR 0656 changes it); the tiers worked out from five live sources (ADR 0603 §6, as ADR 0656 changes it); a seat - list compiled into a part, and every read of another module's bus state the revision view replaces (ADR 0656); the - gate's `merges`, carried into the delivery records and written no more, and its `owed`, said as events; the hold, - the merge turns, their stalled line and tools and the setting `check-gates-merge`, once their record is accepted; - the last three reads of a pull request's description (ADR 0659 §4); every delivery part's call of the forge module's - own tools, for the seat `git`'s verbs; and the four seat-list readers the delivery parts wire by hand. -- §321 At milestone G's delivery, from research 086 (ADR 0666, ADR 0667): the checker (`mesh-delivery-checker`), its seat, - its try with `tryevent` and the indexer's try candidates, its kept verdicts and carries (the runner's from then), the - checking program and `mesh/check`, and its buckets and bus account, removed with the gate's forge merge token only - after the first pipeline after G's delivery has delivered a change end to end and on the operator's one word, its - buckets through `drop` after a snapshot whose restore is tested (ADR 0667 §12); the bundle of steps run before a merge; the Approve per - merge, the milestone ask and its three events, and the gate's merges; the gate's merge states, one key per pull request and its verbs `table`, `recheck` and - `close`; the trunk-only want; `source.main`, for one source revision per watched branch; every fixed branch, - repository and module name in code logic; the recovery merge verb this design sketched, never built; and the holders' - trusted `callers` values, for the seat definitions' `callers`. The words **merge check**, **try**, **trunk commit** - and **merge turn** go to *Not:* lines with the as-is update after that delivery, as the glossary's entries say. - -## What milestone G's builders found open, answered by this design (2026-11-10) - -*ADR 0666, ADR 0667, ADR 0668; ADR 0656, ADR 0596, ADR 0595 §4, §9, ADR 0619 §1, §2, ADR 0563, ADR 0564 §2, ADR 0625 -§1, §4, ADR 0635 §4, ADR 0548 §5, ADR 0363 §2; design 27, *Which module answers*. The operator, 2026-11-10: a small -choice within what a record decides is the design's own, written here with its reason and reviewed by the reviewers -by architectural domain.* - -§322 Milestone G's prepare and develop met these questions (issue 1966). Each line is the design's choice, with the -record it follows from, and replaces the builder's own choice where one was made. - -### The runner and its steps - -- §323 **A step's build seat is read from the artifact's language.** The catalog graph entry names each artifact's - `language`, as its manifest's artifact entry says it and as the indexer's language map matches today; the runner - reads it from the catalog half of its revision view and asks lint and the test steps of the build seat whose - definition's acting rule `build` has `on` naming that language. With no such seat, or no holder of it, the pipeline - ends failed, naming the seat (ADR 0666 §18). Why: the language is already in the manifest and the catalog half is - the runner's one view (ADR 0666 §24, ADR 0596). How it is checked: whole (runner), a Go and a TypeScript fixture - module each ask their own build seat, and one with no holder ends failed naming the seat. -- §324 **An acting rule's `on` may name an artifact kind.** Beside `every` and a language, `on` may be `kind: image`, - matched against the manifest's artifact entry's kind; the seat `node-build-image`'s rule says `kind: image`. Why: an - image has no language, and the rule's fields are this design's (*What ADR 0656 leaves to this design*). How it is - checked: a unit row of the definition reader accepts `kind: image` and refuses another kind. -- §325 **An approval never asked ends `not asked`.** When a `required` step failed before the `approval` step was asked, - the `approval` step ends `not asked`, naming the failed step, never as passed; a `rerun` that passes the failed step - asks it then. `not asked` joins the outcomes of *Pipeline steps and the policy*. Why: approval comes last (above), - and every end a step can have is said as itself (ADR 0666 §10). How it is checked: whole (runner), a failed unit - step leaves the approval `not asked`, and its passing rerun asks it. -- §326 **A build seat keeps its `steps` answer.** The build module keeps each artifact's detected steps in its state - `steps`, from the template `state`, one key per artifact and build-input fingerprint, written at the first - detection and replaced by `steps` with `again`; the module deletes a key unread for 30 days at its next detection. - Why: ADR 0666 §25 asks a tool for "the steps detected for an artifact and detect again", which a reply computed - anew each time would not need; a build module is no foundation part, so the template form (ADR 0666 §4). How it is - checked: whole (Go build module), `steps` answers from the key, and `again` replaces it. -- §327 **The step code lives in the domain SDK.** The step run and the fetch-and-release code the three build modules - share move, in this milestone, into a build-step package of `mesh-delivery-sdk`, which the Go, TypeScript and image - build modules import; none keeps a copy. Why: code shared between modules lives in an SDK, and the delivery - domain's shared code is `mesh-delivery-sdk`'s (ADR 0635 §4); the copies were met in develop. How it is checked: a - search of the three build modules finds no fetch, release or step-run function of their own. -- §328 **Lint per language.** Go's lint step is the Go build module's linter with the fixed-names rule; TypeScript's is the - type-check with the fixed-names rule; an image's lint is the fixed-names rule over its build files; - an image has no unit or integration test step, so `steps` answers none for it and the seed has those `bypass` - (*A module's registration*). Why: ADR 0666 §5 has the lint step refuse fixed names in every language, and §7 lets a - build seat answer only the steps an artifact can run. How it is checked: whole (TypeScript and image build modules), - a fixture naming a module in code fails lint. -- §329 **What the fixed-names rule exempts.** It exempts a Go import path, a comment and a test file (`_test.go`, - `*.test.ts`), never a struct tag or any other string of code. Why: none of the three is code logic (ADR 0666 §5), - and a test fixture names modules. How it is checked: unit (lint rule), a name in a comment and in a test file - passes, in a struct tag fails. -- §330 **The runner's own kept data age by its own deletes.** The runner deletes its own `pipelines` keys, `kept` entries - and `step-outputs` objects by the retentions of *The runner's kept data*, at each step's end, for those past their - retention; a foundation part's sent store (`sent-objects`) is aged the same way by its owner at each send. Nothing - waits on a timer, and nothing but the owner deletes. Why: an old section cannot declare an age, and deleting one's - own entries is the owner's act, not the removal of a store ADR 0595 §4 holds to a person's word. How it is checked: - whole (runner), an output 7 days old is gone after the next step's end. -- §331 **The kept event streams' caps.** On the seat `mesh-pipeline-runner`, each kept 90 days: `pipeline-started` 32 MiB, - `pipeline-ended` 128 MiB, `group-ended` 64 MiB, `run-started` 16 MiB, `step-started` and `step-ended` 128 MiB each, - `wait-held` and `wait-ended` 32 MiB each; the seat `git`'s `branch-moved` 64 MiB, 90 days. Why: about 300 pipelines - a day of about 6 steps (*The runner's kept data*) say about 1,800 step events a day of about 0.5 KiB, about 80 MiB in - 90 days, and about 300 pipeline ends of about 4 KiB, about 105 MiB; estimates, read from the fill tools once G runs. - How it is checked: each seat definition's fixtures name each stream's `keep`. -- §332 **A group's end is said under the group.** `group-ended` is said on - `mesh.seat.mesh-pipeline-runner.event.group-ended..`, with no module part, since a group spans - modules. Why: every other runner event names one module (*Health and going back*). How it is checked: the seat's - fixtures. -- §333 **The runner hears the indexer's own event.** The runner's manifest `consumes` the indexer's own event - `take-in.taken` (ADR 0666 §13), which ADR 0596 §1 lets any module subscribe to; the indexer's name stands in that - manifest only, never in the runner's code. Why: ADR 0666 §13, in force, names that event. How it is checked: a search - of the runner's code finds no module name (lint). -- §334 **A publication's pipelines and their key.** `package-published` carries the package's artifact reference - (`:`, from the catalog entry), its version and digest, the watched branch and the source revision - its build was made from. The runner reads the dependents from that revision's catalog graph, the artifacts with a - kind-1 edge to the package, and starts each one's pipeline keyed by that branch, that source revision and the - dependent module, the version in the pipeline's state; a `take-in.taken` of the same revision and module is the - same pipeline (*A second start writes nothing*). Why: a pipeline's key is branch, source revision and module (above), - and the publication is made from a revision of a watched branch (ADR 0666 §15). How it is checked: whole (builder, - runner), a publish starts one pipeline per dependent, keyed by the publishing revision. -- §335 **What the teller says of a widening.** Each entry of `widens` names what widens (`acting-rule`, `grant`, - `credential` or `serving-part`), its name, the module and the commit. The teller says one message per - `pipeline-ended` that carries any, on every channel, once per module, watched branch and commit. Why: ADR 0666 §23 - says a widening in the pipeline's end, and the teller passes it on (*Who may say what*). How it is checked: whole - (teller), a `pipeline-ended` said twice with the same `widens` gives one message. - -### Callers - -- §336 **The forms of `callers`.** A verb's or an accepted kind's `callers` is a list whose entries are: a holder role (a - seat's name), a module, `operator`, `healers`, `persons` (any person the tool runner proves), `sessions` (an agent - session the tool runner proves), `invokers` (every module whose manifest declares the verb in `invokes`), and - `holder:@same-node` (the holder of that seat on the node of the verb's holder). A definition whose `callers` - today is text is given a list in this milestone: "any caller whose role grants it" becomes `invokers`, "a person" - `persons`, and every other text the names the design gives that verb. Why: ADR 0666 §20 makes the definition the - grants part's one source of caller limits, in a form it reads. How it is checked: unit (definition reader), each - form accepted and any other entry refused; the grants part's table test grants each form. -- §337 **The bus module's restore points.** The verbs the holder of `node-backup` on the bus module's node calls to take - and restore a module's kept data on the bus name `holder:node-backup@same-node`. Why: only that holder takes them - (*A backup before the apply: kept data on the bus*). How it is checked: a call from another node's `node-backup` - holder is refused by the bus. -- §338 **Callers on accepted kinds.** An accepted kind of a work queue carries `callers` as a verb does, and the grants part - grants publish on its subject to them alone: the build seats' `step` the runner; `run` the runner, the indexer and - the assigner, and the checker until it goes; `mesh-wants`'s `step` the runner. Why: a step's result is taken only - from its seat holder (ADR 0666 §21), so the ask side is limited too. How it is checked: a `step` published by another - account is refused by the bus. -- §339 **The contract generator reads `callers` and makes nothing of it.** The generated contracts carry no caller check; - the grants part reads `callers` from the definition, and the generator refuses a definition whose entry is no form - above. Why: one source of caller limits (ADR 0666 §20). How it is checked: the generator's test refuses an unknown - entry. -- §340 **The reader release first.** mesh-sdk's seat-definition reader released before G's check reads `callers` as text - or as a list; G's own reader refuses text. Why: readers first (ADR 0667 §4), and every definition is a list from G. - How it is checked: unit rows for both forms in the release, and for the refusal in G. -- §341 **TypeScript contracts as JavaScript.** The generator emits each seat's TypeScript contract as `index.js` with its - `index.d.ts`, so a TypeScript module imports it with no build step of its own. Why: a seat's contract is generated - from its definition (ADR 0625 §1), and a gap of the build system is built in it. How it is checked: a TypeScript - module builds against a generated contract with no `tsc` of the contract. - -### The catalog, the builder and the assigner - -- §342 **A module not watched on a branch.** A watched branch's catalog graph takes each module not watched on that branch - from the registration's first watched branch. `register` names a module's watched branches; with none named it takes - the repository's default branch, read through the seat `git`. Why: a placement's default is the registration's first - branch (ADR 0666 §6). How it is checked: whole (indexer), a `beta` revision holds a `main`-only module at `main`'s - head. -- §343 **Branch names the indexer's keys use.** `register` and `watch` refuse a watched branch named `candidate` or - `entry`, naming why, since the catalog keeps its revisions under `.` beside those two key forms. - How it is checked: whole (indexer), each is refused. -- §344 **The bus module plans a sent store from what a manifest already says.** It plans `_sent-objects` for a - module of the new form whose accepted manifest claims a seat whose definition has a verb or an event, or declares an - event of its own, read from the manifest and the seat list; the indexer's event carries no field `sends`. Why: no - manifest field (*Any size*). How it is checked: whole (bus module), a module claiming a seat with a verb gets a sent - store, one with neither gets none. -- §345 **A large value reaches the tool runner in chunks.** A bundle's put of a value over the bus's per-message limit is - sent to the tool runner in chunks under that limit, in order, each acknowledged, and written as one object through - the object store's own chunking; a put not sealed within the call's bound is dropped by the tool runner. No value is - split over objects. Why: one object per value (ADR 0666 §16), through the tool runner (ADR 0595 §9). How it is - checked: whole (mesh-sdk), a value of a hundred times the limit is read back whole. -- §346 **One object store package.** The assigner's and the indexer's object store code is mesh-sdk's small object store - package, with no copy in either. Why: ADR 0595 §9. How it is checked: a search finds no second copy. -- §347 **Reading sent objects is a bus state call.** mesh-sdk lists the call that reads a sent object under its existing - bus state capability. Why: ADR 0595 §9 puts the calls in that package. How it is checked: the package's table test. -- §348 **The builder reads the catalog through its seat.** The builder reads the registrations' watched branches through - `mesh-catalog`'s `registrations` and `registration-changed`, and a build seat's holder from the seat list; the - manifests, the packages, each package's name in its ecosystem (the Go module path, the npm name, which the catalog - graph entry's packages carry) and a run's candidate from the catalog half of its revision view, never the indexer's - state. With no holder of a build seat, a build ends `could not run`, naming the seat, and is asked again on the seat - list's change. Why: ADR 0596 §2. How it is checked: the builder's account holds no grant to the indexer's state. -- §349 **`package-published` is heard by the indexer.** The indexer keeps its package state from `package-published` for - every package, and from the builder's `version.numbered` only for images, bundles and archives. Why: the - publication is the seat's event (ADR 0666 §15). How it is checked: whole (indexer), a published package's version is - in its state. -- §350 **A superseded build's drop is a kept call.** The runner ends a superseded build's step `superseded` at once, and - asks the build seat's `drop` as a kept call (ADR 0668 §1, §2), so it is asked again through every transport fault - and said `call-unanswered` after five minutes. How it is checked: whole (runner), a drop unanswered is asked again. -- §351 **One rule for asking again.** mesh-sdk's kept-call form (ADR 0668 §7) asks an ask again only while it is open and - not replaced, and every asker and every hand ask-again tool uses it. How it is checked: unit (mesh-sdk), a replaced - ask is not asked again. -- §352 **The assigner's newer-revision check reads the want.** Each want names its source revision and watched branch, and - the assigner writes a want only by compare-and-set over a want of an older revision of that branch (*What holds a - delivery*). How it is checked: whole (assigner), an older revision ending after a newer one writes nothing. -- §353 **The assigner follows the registrations.** `mesh-delivery-contracts`' client `meshcatalogclient` carries a typed - follower of `registrations`, a first view then `registration-changed`, built on `go/views`; the assigner and the - forge module's merge switch on its first registrations (ADR 0667 §10). How it is checked: whole (assigner), it keeps - the trunk-only want until the first `registration-changed`. -- §354 **The assigner's composition step runs the composer in the sandbox.** It asks the seat `node-build-go` the ask `run` - (*What ADR 0656 leaves to this design*), its step keys under `assigner.`, and serves the ask's input by its seat - `mesh-wants`' verb `input`, kept a day in its state `inputs`, as the checker's seat does today; the indexer does the - same for its validation by `mesh-catalog`'s `input`. The Go build module reads no revision verb. Why: the - composition step runs on parts built from the heads, in the sandbox (ADR 0666 §17). How it is checked: whole - (assigner), a composition step's `run` is answered and its input read by digest. -- §355 **The assigner says an unanswered `manifest-reads`.** The assigner, the one caller of the node-engine's - `manifest-reads` from G, writes `call-unanswered` into its own findings state, and the condition keeper's reads gain - it in the same change (ADR 0668 §3). How it is checked: whole (assigner), a node-engine that never answers raises it - after five minutes. -- §356 **Two acting parts in different steps make no cycle.** The assigner's cycle check counts waits within one step only - (*A wait holds only within its step*). How it is checked: the unit rows there. -- §357 **Which provider a requirement points to.** In the assignment graph, by design 27's order: the named seat's - holder, a pin, the provider on the consumer's node, the only provider; otherwise the entry is held, naming the - candidates, and nothing is guessed. A contribution edge runs from a consumer's entry to the provider its requirement - resolves to, wherever that requirement carries values the consumer contributes. How it is checked: unit - (assignment graph), each step of the order and a hold. -- §358 **The catalog's shared view takes a branch.** The client's view of the catalog half is per watched branch, named by - its caller. How it is checked: unit (client), two branches give two views. -- §359 **`difference` names its revisions.** `difference` with none of `from`, `to` and `group` is refused, naming the - missing argument. How it is checked: a unit row. -- §360 **An approved ask binds the placement.** What an approved ask binds to includes the placement's watched branch and - its `build`, beside the fingerprint. How it is checked: whole (assigner), a placement changed after the Approve asks - again. -- §361 **A placement's `build` is a held put.** A placement's `build` is written as a held put of that build, released when - the placement drops it; a put a person wrote is never released by a placement change. How it is checked: whole - (assigner). -- §362 **The indexer's first-start catch-up runs apart from its source handler.** At its first start the indexer takes in - the heads the gate's source names by its catch-up path before it starts its handler of `source-moved`. How it is - checked: whole (indexer). -- §363 **A stale ask is dropped, backups included.** The assigner drops an ask it raised once the want it asked for is - replaced, a backup ask among them (built in mesh-delivery #200). Why: ADR 0596 and ADR 0668 §2. How it is checked: - whole (assigner). - -### The condition keeper and the node-engine - -- §364 **The node-engine claims its seat.** The node-engine's manifest claims the node seat `node-engine` (ADR 0563), and - the indexer and the node-engine's own guard read its holder from the seat list, keeping no module name. How it is - checked: lint finds no module name in either. -- §365 **The condition keeper reads its sources from the catalog.** The catalog graph entry carries each kept data a - manifest declares (its name and its section or template); the keeper takes a first view of the catalog half and - reads a module's `findings` wherever its entry declares a state `findings`, and the node-engine's holder from the - seat list. It joins the readers of *Every other reader of which modules exist*. Why: no fixed names (ADR 0666 §5), - and the keeper reads findings (ADR 0564 §2). How it is checked: whole (keeper), a fixture module declaring `findings` - is read with no code change. +The part is six files, each read whole, its design lines numbered across them: + +- [G-1-pipelines-registration-and-steps](G-1-pipelines-registration-and-steps.md): pipelines per module: registration, watched branches, steps and policy, what holds a delivery, approval, groups, dependencies, a publish. +- [G-2-pipelines-sizes-checks-and-tools](G-2-pipelines-sizes-checks-and-tools.md): pipelines per module: any size, the pipeline makes the check, who may say what, health, recovery, tools, kept data, how G itself is delivered, the choices under ADR 0666 and ADR 0667. +- [G-3-delivery-record-and-graphs](G-3-delivery-record-and-graphs.md): the delivery record, what the gate owes the forge, the state table, the two graphs and a change's graph difference. +- [G-4-revision-view-and-steps](G-4-revision-view-and-steps.md): the revision view, what ADR 0656 leaves to this design, the choices under ADR 0656, what each step says and their tools. +- [G-5-acting-parts-check-and-kept-data](G-5-acting-parts-check-and-kept-data.md): the acting parts, the removed hold, the check on the change's own acting parts, after the merge, taken in, and kept data on the bus. +- [G-6-verbs-retires-and-answers](G-6-verbs-retires-and-answers.md): the verbs G adds, what retires, and the answers to the builders' and the reviewers' questions. -- 2.54.0 From e27fd022697ec9f6eb5ab0d76ff8a4776c8cd50a Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 21:36:28 +0100 Subject: [PATCH 03/13] Number a module's version before its build, as the operator chose, so packed files carry it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Go module zip and a TypeScript package hold their version inside the file, so a number given at publication could not be valid for a digest built before it (gaps items 22, 23, 43). The operator chose on 2026-11-10: the manifest says MAJOR.MINOR, the build system reserves the next patch at the build's start, the catalog holds and says released versions only, the first watched branch alone releases, and Go packages are tagged, departing from ADR 0573 §2 until a later research brings the record in line. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../47-delivery-from-commit-to-delivered.md | 11 ++++- .../G-1-pipelines-registration-and-steps.md | 17 ++++++-- .../G-2-pipelines-sizes-checks-and-tools.md | 2 +- .../G-6-verbs-retires-and-answers.md | 41 ++++++++++++++++--- 4 files changed, 59 insertions(+), 12 deletions(-) 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 fd70539e..3b1cae42 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 @@ -1847,7 +1847,8 @@ milestone with more tests than it starts with (research 085, Direction, One mile other's new verb are delivered together as one step; two artifacts each needing the other's new package to build are refused, naming both; a seat moved to a new holder has its old holder released before the new one claims (unit, the indexer's graph; whole, assigner, mesh-delivery; live). -80. **R18, a publish rebuilds.** A mesh-sdk merge changing one package publishes one version, and only the artifacts +80. **R18, a publish rebuilds.** A mesh-sdk merge changing one package publishes one version, reserved before its + build (test 102), and only the artifacts importing that package rebuild against it and are delivered, with no hand act; a merge changing only tests rebuilds nothing (whole, builder and indexer, mesh-delivery; live). @@ -1919,6 +1920,12 @@ ADR 0667's checks, of milestone G itself: 101. **A second start writes nothing.** A `take-in.taken` and a `package-published` for the same source revision and module start one pipeline: the second start's compare-and-set on the pipeline key writes nothing (whole, runner, mesh-delivery). +102. **A version is reserved, carried and released** (design 47 G §52, §349, §368 to §371). A module whose manifest + says `1.4`, with `1.4.2` the highest released, builds as `1.4.3`, its Go package tagged `v1.4.3` and its program + reporting it; with one of its two artifacts published `1.4.3` is not released, and `builder_publish_again` + publishes the other under `1.4.3`, which the catalog then says `version-released` once; a new `1.5` builds as + `1.5.0`; two builds close together reserve two versions; a `beta` build reserves none and is delivered; a manifest + saying `1.4-beta` is refused (whole, builder, indexer and Go build module, mesh-delivery and mesh-catalog). ## What retires @@ -2014,7 +2021,7 @@ release reports the controller's code lines against research 063's baseline (acc | L6 | Provisioning leaves the controller (ADR 0570): every provision is one key of the seat `mesh-provisioner`, held by mesh-provisioner, holding a reference and never a value, and each placed module's provisioning plan a key of its state `plans`, both composed from placement, the wants and the manifests on change events; each provider acts once all a provision depends on is provided; the assigner moves a want only once its build's provisions are provided; a provision no want needs is retiring, and its account or credential is removed only on the operator's Approve, its data never; the vault mints each provision's secret sealed to the consumer's and the provider's node, and the consumer's node-engine writes it; a wait for a person is a provision's state, which the watchman reads; bus permissions stay with the grants part (ADR 0349, ADR 0230, ADR 0547 §11) | — | mesh-sdk, the node-engine (`mesh-host`), mesh-tools (the tool runner's provisioner client), mesh-catalog (mesh-provisioner, the vault, the provisioner client, postgres, keycloak, gitea, route-adapter), mesh-delivery (assigner, watchman) | 1954 | | L7 | History and records leave the controller (ADR 0571): the builder keeps its build log, one entry at a build's start and one at its end, from which build history and durations are read and the module mesh-catalog asks the builds it missed; the artifact sweep is the seat `mesh-artifact-sweep`, held by artifact-store-tools beside the artifact store, its keep set from the store's own listing, the wants and the build log, sweeping at each build's end from the lease write and recording each deletion in the hand-act log; disk readings are disk-load's own state on every node; who holds which seat is the state `seat-holders` of the seat `mesh-placement` (now `mesh-module-assigner`), and tools are found by the tool runners' announcements; what a node runs is its want and its have; the grants part writes `grants..` and keeps memberships in its own state; root-free is a probe of the self-check; no manifest but the controller's reads, invokes or consumes `mesh-controller`, but a named list kept to the lease write, so the lease write's check passes (ADR 0189, ADR 0547 §11, ADR 0549 §6) | — | mesh-sdk, mesh-controller (its pin only), mesh-delivery (builder, indexer, assigner, grants part, view), mesh-catalog (artifact-store-tools, disk-load, the backup seat's holder, mounts, nfs-server, minio, mesh-watcher, the messenger, the self-check, the module mesh-catalog, docker), mesh-tools (the tool runner) | 1955 | | B0 | The bootstrap delivery: every check, a lone pull request's as a group of one and a verdict carried after a trunk move included, is judged by a checking program built from the change's own heads in the build seat's sandbox, which also makes the heads' one seat list that the check hands the manifest validator and the composer; it brings no seat definition and no manifest field, so the running checker checks it (ADR 0644; *The bootstrap delivery, and the one milestone after it*) | 26 needs it | mesh-sdk (`go/delivery`: the seat `node-build-go`'s ask `run` and the checker's record of which checking program judged; `go/composer`: the request's seat list), mesh-catalog (mesh-build-go: the run step, the refused answer, the kept programs' tools and sweep), mesh-delivery (checker: the checking program and its command), hq (this design, the glossary) | 1921 | -| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check; and, one milestone with them, research 086's pipelines: a change of any module goes from a commit on a watched branch to the machines through its module's pipeline, its policy in its registration, ordered by edges between artifacts, with no Approve per merge and no hand act, the checker gone (ADR 0666, ADR 0667). Test counts at its start (G's prepare, 2026-11-09): mesh-delivery 665 unit and 68 integration, the node-engine (`mesh-host`) 1,742 unit; each other repository's and each catalog module's from the last check's verdict, read in the joint prepare | 26 to 62, 63 to 100 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker until its removal, builder, grants part, gate's view, forge bridge, watchman, the new pipeline runner `mesh-delivery-pipeline`, `contracts` and `sdk` moved under `modules/`), mesh-catalog (the gate, the build modules, the forge module, mesh-issues, mesh-knowledge, the `node-backup` holder, the router, nats: a module's kept data on the bus as a backed-up item and the object store beside each state), mesh-sdk (also its small object store package, for values of any size), the node-engine (`mesh-host`), hq | 1966, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | +| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check; and, one milestone with them, research 086's pipelines: a change of any module goes from a commit on a watched branch to the machines through its module's pipeline, its policy in its registration, ordered by edges between artifacts, with no Approve per merge and no hand act, the checker gone (ADR 0666, ADR 0667). Test counts at its start (G's prepare, 2026-11-09): mesh-delivery 665 unit and 68 integration, the node-engine (`mesh-host`) 1,742 unit; each other repository's and each catalog module's from the last check's verdict, read in the joint prepare | 26 to 62, 63 to 102 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker until its removal, builder, grants part, gate's view, forge bridge, watchman, the new pipeline runner `mesh-delivery-pipeline`, `contracts` and `sdk` moved under `modules/`), mesh-catalog (the gate, the build modules, the forge module, mesh-issues, mesh-knowledge, the `node-backup` holder, the router, nats: a module's kept data on the bus as a backed-up item and the object store beside each state), mesh-sdk (also its small object store package, for values of any size), the node-engine (`mesh-host`), hq | 1966, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | An issue is in one row only (ADR 0493 §2): where a row named an issue another row names too, the last row keeps it and the earlier row says so in words (the operator, 2026-10-26). Milestone M1 makes each row a record of the issue tracker diff --git a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md index 12e31640..9c3ebcbe 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md +++ b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md @@ -378,10 +378,19 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l ### A publish rebuilds -- §52 **Numbered at publication.** The builder numbers a package's version when it is published, never only when a build - is claimed, and says the publication as the event `package-published` of its seat `mesh-builds` (ADR 0666 §15; design - 51, *The versions*). The seat is new, held once for the mesh, defined in `mesh-delivery-contracts`; the builder held - none until now, and its waits stay on its own stream in this milestone. +- §52 **A version is reserved before the build and released when every artifact is published.** A module's version + is its manifest's `version`, `MAJOR.MINOR`, and a patch the build system gives: the next after the highest version + the catalog holds released for that `MAJOR.MINOR`, 0 for one it holds none of. The builder reserves it when the + module's build starts and passes it to the build of every artifact of the module (§370), so code and packed files + carry it; each artifact published is said as the event `package-published` of the builder's seat `mesh-builds` + (ADR 0666 §15), and the version is released only once every artifact of the module at it is published (§349); only the first watched branch releases (§371). A + failed publish leaves the version reserved, and `builder_publish_again` publishes the missing artifacts under the + same version. The seat `mesh-builds` is new, held once for the mesh, defined in `mesh-delivery-contracts`; the + builder held none until now, and its waits stay on its own stream in this milestone. Why: the operator, 2026-11-10 + about 21:40 CET, a design choice of G's part (gaps items 22, 23 and 43). This reserves the number when the build + starts, where ADR 0666 §15 numbers a package at its publication and design 51 publishes a held digest under a new + number: a packed file carries its version, so it must be known before the build; the operator's choice, which a + later research brings the records in line with. How it is checked: test 102. - §53 **Every dependent is rebuilt against it.** On `package-published` every artifact with a kind-1 edge to the package is rebuilt against the new version, and the runner, which consumes the event, starts its pipeline; nothing else is rebuilt, and a change to tests alone publishes nothing (ADR 0666 §15; research 086, R18). diff --git a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md index e8f80277..62a42ae3 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md +++ b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md @@ -199,7 +199,7 @@ four lines a module's tools answer; every read tool of a store also answers its | the forge module (`git`) | `git_failed_steps` (the failed `required` steps it holds, by module and fingerprint, with the run that failed each) | — | `git_failed_steps_drop` (drop one, with why) | | the grants part (`mesh-grants`) | `grants_callers` (the callers each verb admits, read from the seat definitions) | — | `grants_why_refused` (for an account and a verb, the grant it lacks and the definition's line that leaves it out) | | the build seats | `steps` (the steps detected for an artifact) | `steps` with `again` (detect again) | `drop` (ADR 0664 §4) | -| the builder (`mesh-builds`) | `builder_versions` (a package's published versions) | `builder_publish_again` | — | +| the builder (`mesh-builds`) | `builder_reserved` (each module's reserved version, §369); the released versions are the catalog's `versions` | `builder_publish_again` (publish the missing artifacts under the reserved version) | — | | the assigner (`mesh-wants`) | `assigner_why_not` (why a module is not on a machine yet: the pipeline it waits for, its hold, the placement, the node's last `running`) | — | `assigner_release` | | the watchman | `watchman_views` (how far behind each part's view is, from each part's `view-waits` and first-view tools) | — | each part's `_first_view_again` | | the operator-channel | `asks` (with each asker's asks held back) | — | `ask-again` (offer a held-back ask now) | diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md index 067b048e..96f39caa 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md @@ -382,15 +382,22 @@ record it follows from, and replaces the builder's own choice where one was made - §347 **Reading sent objects is a bus state call.** mesh-sdk lists the call that reads a sent object under its existing bus state capability. Why: ADR 0595 §9 puts the calls in that package. How it is checked: the package's table test. - §348 **The builder reads the catalog through its seat.** The builder reads the registrations' watched branches through - `mesh-catalog`'s `registrations` and `registration-changed`, and a build seat's holder from the seat list; the + `mesh-catalog`'s `registrations` and `registration-changed`, the released versions it numbers from through + `versions` and `version-released`, keeping no list of versions of its own, and a build seat's holder from the seat list; the manifests, the packages, each package's name in its ecosystem (the Go module path, the npm name, which the catalog graph entry's packages carry) and a run's candidate from the catalog half of its revision view, never the indexer's state. With no holder of a build seat, a build ends `could not run`, naming the seat, and is asked again on the seat list's change. Why: ADR 0596 §2. How it is checked: the builder's account holds no grant to the indexer's state. -- §349 **`package-published` is heard by the indexer.** The indexer keeps its package state from `package-published` for - every package, and from the builder's `version.numbered` only for images, bundles and archives. Why: the - publication is the seat's event (ADR 0666 §15). How it is checked: whole (indexer), a published package's version is - in its state. +- §349 **The catalog holds released versions only, and says each.** The indexer keeps, for the seat `mesh-catalog`, + each module's released versions in its old `state` section `versions`, one key per module and `MAJOR.MINOR` holding + its released patches with each artifact's digest and the source revision built (issue 1961's catalog of versions). + It hears every artifact's `package-published` (packages, images, bundles and archives alike) and records a version + as released once every artifact the module's catalog entry names is published at it, then says `version-released` + (the module, the version, its artifacts, the source revision) on `mesh.seat.mesh-catalog.event.version-released`, + kept 90 days at 16 MiB; the verb `versions` answers a module's released versions, its callers the builder and any + person or session. A reserved version is not in it. The builder's `version.numbered` is no longer said or heard. + Why: the operator, 2026-11-10 about 21:40 CET (gaps items 23 and 43). How it is checked: whole (indexer), a version + with one of two artifacts published is not released, and with both is, said once. - §350 **A superseded build's drop is a kept call.** The runner ends a superseded build's step `superseded` at once, and asks the build seat's `drop` as a kept call (ADR 0668 §1, §2), so it is asked again through every transport fault and said `call-unanswered` after five minutes in the runner's findings. How it is checked: whole (runner), a drop @@ -476,3 +483,27 @@ record it follows from, and replaces the builder's own choice where one was made itself answered as lost stays lost, said in the indexer's journal and in `indexer_pending`. G's own change for it (`LostAgain`, commit 106fc16b) is not built. Why: one rule for kept calls (ADR 0668), already on main. How it is checked: the indexer's take-in table, a transport fault reads the message again and a lost answer does not. +- §368 **A manifest says its `MAJOR.MINOR`.** Each module's manifest names `version`, two numbers `MAJOR.MINOR`; the + manifest validator refuses any other form, a pre-release or custom suffix (`beta`, `alpha`) among them: no custom + version exists for now. A manifest without it is refused once G's reader runs (ADR 0667 §4: the reader is delivered + first). Why: the operator, 2026-11-10 about 21:40 CET. How it is checked: unit (manifest validator), `1.4` accepted, + `1.4-beta` and `1.4.2` refused. +- §369 **The reservation is one key per module and `MAJOR.MINOR`.** The builder, a foundation part, keeps in its old + `state` section `reserved` one key per module and `MAJOR.MINOR`, the version its build reserved, written by + compare-and-set at the build's start; a newer build of the module takes the next after the higher of the highest + released and the one reserved, so two builds never share a version, and a superseded build's version is never + released, leaving a gap in the patches. The builder deletes the key when the catalog says `version-released` for it; + `builder_reserved` reads the keys. This is no list of released versions: numbering reads those only from the + catalog. Why: the operator, 2026-11-10 (a version reserved when the build starts); the key and the gap are the + design's choice. How it is checked: whole (builder), two builds close together reserve two versions, and the key + goes on `version-released`. +- §370 **The version reaches every artifact's build.** The build seat's ask carries the version, and the build module + passes it to the artifact's build as the build argument `MESH_VERSION`: a Go package is tagged `vMAJOR.MINOR.PATCH`, + an npm package's version is it, an image carries it as its tag and label, and a program reads it as its own version. + A Go package's version is its tag: this departs from ADR 0573 §2, which has a Go package's version be its trunk + commit, by the operator's choice of 2026-11-10, and a later research brings that record in line. How it is checked: + whole (Go and TypeScript build modules), a built package and program carry the reserved version. +- §371 **Only the first watched branch releases.** Only a build of the registration's first watched branch reserves and + releases a version; a build of another watched branch reserves none, publishes no version and is delivered by its + build, its fingerprint, as every want names one. Why: the operator, 2026-11-10. How it is checked: whole (builder), + a `beta` build reserves nothing and is delivered. -- 2.54.0 From c46669a17e26a971379dbd077002655b37c3f03b Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 21:37:54 +0100 Subject: [PATCH 04/13] Have design 51 reserve a module's version at its build's start, as design 47's milestone G does Design 51 still numbered a package at its publication and said `version.numbered`, which the operator's choice of 2026-11-10 replaces: the catalog's released versions are the one list, and a packed file carries its version. The catalog's first versions are filled from what is published, so numbering continues above today's. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../G-6-verbs-retires-and-answers.md | 6 +++ .../51-the-registries-keep-what-is-named.md | 44 +++++++++++-------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md index 96f39caa..bc07c3cf 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md @@ -507,3 +507,9 @@ record it follows from, and replaces the builder's own choice where one was made releases a version; a build of another watched branch reserves none, publishes no version and is delivered by its build, its fingerprint, as every want names one. Why: the operator, 2026-11-10. How it is checked: whole (builder), a `beta` build reserves nothing and is delivered. +- §372 **The catalog's versions start from what is published.** At the new indexer's first start, `versions` is filled + once, as released, from each name's highest `published` version the builder's state `versions` held, read by + `builder_versions`; it is not filled again. Why: numbering reads only the catalog's released versions (§348), and + without this every first version under G would start below today's and Go's `@latest` would never move (the + design's choice, 2026-11-10). How it is checked: whole (indexer, builder), with `0.1.14` published before G, the + first G build of `0.1` is `0.1.15`. diff --git a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md index 580bf6dd..9c89370d 100644 --- a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md +++ b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md @@ -6,7 +6,7 @@ code: - mesh-catalog modules/artifact-store-tools (the store's tools), modules/distribution (its program, holding the two image seats) - mesh-catalog modules/gitea (package retention; the two package seats' verbs in its Go bundle) - mesh-catalog modules/mesh-build-go, modules/mesh-build-node, modules/mesh-build-image (publishing through the seats) - - mesh-delivery modules/mesh-delivery-builder (versions, tag asks; from milestone G a package numbered at its publication, the seat `mesh-builds`), the forge bridge, the indexer, the checker, the assigner, modules/mesh-delivery-pipeline (`tag-wanted` from milestone G) + - mesh-delivery modules/mesh-delivery-builder (versions, tag asks; from milestone G a module's version reserved at its build's start and released by the catalog, the seat `mesh-builds`), the forge bridge, the indexer, the checker, the assigner, modules/mesh-delivery-pipeline (`tag-wanted` from milestone G) - mesh-sdk (the bus helper for sending in parts) - mesh-catalog modules/docker (image pruning) - mesh-host internal/apply (every new image of a module pulled before any of its containers is replaced; a failed pull applied again on the status view's `settled` for the store's holder, and at the node-engine's start), internal/compose (each routed image reference written with its seat and that seat's holder) @@ -266,24 +266,30 @@ the verb. ### The versions -- **From the delivery of design 47's milestone G, a package is numbered when it is published** (ADR 0666 §15). The - builder numbers a package's version at its publication, never only when its build is claimed: a package's trunk - build is claimed with no number and built by its digest, and once its build is done the builder gives it the next - count of its series, under compare-and-set, and says `numbered`, on which the build module calls `publish` with - the version and the held digest, as below for a floor not yet known. A build of an older source revision of the - same watched branch than one already numbered for that name is not numbered and publishes nothing, so a version never - names older content than the one below it. When the holder says `published`, the builder says the publication as the - event `package-published` of its seat `mesh-builds` (the name, the version, the fingerprint, the commit, the watched - branch). On it every artifact with a "published before it builds" edge to the package is rebuilt against the new - version, and the pipeline runner, which consumes the event, starts its pipeline (ADR 0666 §15; design 47, *A publish - rebuilds*); - nothing else is rebuilt, and a change to a package's tests alone publishes nothing. This removes the claim's wait - for earlier merges' numbers below, which held a group on 2026-11-09 (research 086, Findings). Why at publication: a - number given at the claim must wait for every earlier merge's number, and a build that never publishes keeps a - number nothing holds. An image, bundle or archive keeps the rule below. How it is checked: in the builder's - whole-module test a package's trunk build is numbered only once built, an older revision's build after a newer one - publishes nothing, `package-published` is said once per publication, and only the importing artifacts rebuild - (design 47, test 80). +- **From the delivery of design 47's milestone G, a module's version is reserved at its build's start and released + when every artifact is published** (the operator, 2026-11-10 about 21:40 CET; design 47 G §52, §348, §349, §368 to + §371). A module's manifest says its `version`, `MAJOR.MINOR`, and no custom version (`beta`, `alpha`) exists for now + (G §368). The patch is the next after the highest version the catalog holds released for that `MAJOR.MINOR`, 0 for + one it holds none of; the builder reserves it when the module's build starts, in its key `reserved` for the module + and `MAJOR.MINOR` (G §369), and passes it to the build of every artifact as a build argument, so code and packed + files carry it (G §370): a Go package is tagged `vMAJOR.MINOR.PATCH`, in git under its module directory's prefix + (`go/vMAJOR.MINOR.PATCH`), which departs from ADR 0573 §2 (a Go package's version is its trunk commit) by the + operator's choice, a later research bringing the record in line. Each artifact published is said as + `package-published` of the seat `mesh-builds` (ADR 0666 §15), on which every artifact with a "published before it + builds" edge to it is rebuilt against the new version and its pipeline started (design 47, *A publish rebuilds*); + nothing else is rebuilt, and a change to a package's tests alone publishes nothing. The version is released only + once every artifact of the module is published at it: the catalog, the seat `mesh-catalog`, then holds it and says + `version-released`, and its `versions` is the one list of released versions, which numbering alone reads (G §348, + §349). A failed publish leaves the version reserved, and `builder_publish_again` publishes the missing artifacts + under the same version. Only a build of the registration's first watched branch reserves and releases a version; a + build of another watched branch is delivered by its build, with none (G §371). The builder no longer numbers a + build at its claim or at its publication, says no `numbered` or `version.numbered` for a module's versions, and + keeps no list of them of its own: the bullets below on the claim's numbering, the numbering per source revision, the + builder's state `versions`, the floor and the event `numbered` hold until that delivery only. Numbering continues above what is published today, the catalog filled once from it (G §372). Why reserved before the build: a Go module zip and a TypeScript package carry their + version inside the file, so a number given at publication could not be valid for a digest built before it; this + departs from ADR 0666 §15's numbering at publication by the operator's choice. How it is checked: design 47's test + 102, and in the builder's whole-module test a version is reserved at the build's start, released once both of a + module's artifacts are published, and published again under the same version after a failed publish. - **A build that cannot read the published packages refuses**, naming the unread state, never building against a guess (ADR 0666 §15; design 47, test 70). - **Until that delivery, the builder numbers every trunk build** of a package, image, bundle or archive at its claim: the package series -- 2.54.0 From f215cb9207754fc1114d3b415f5adf18f84ae6fb Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 21:39:09 +0100 Subject: [PATCH 05/13] End tagged versions at milestone G, since no custom versions exist for now The operator's answer of 2026-11-10 allows no custom version, and a tagged version is one: a group's run on request delivers nothing, so it builds its members against each package member's build by digest and publishes no version, and `tag-wanted` and `tag-asks` go with G's delivery. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../47-delivery-from-commit-to-delivered.md | 6 +++++- .../G-2-pipelines-sizes-checks-and-tools.md | 2 +- .../G-6-verbs-retires-and-answers.md | 12 +++++++++++- .../51-the-registries-keep-what-is-named.md | 19 +++++++++++-------- 4 files changed, 28 insertions(+), 11 deletions(-) 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 3b1cae42..eee387ed 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 @@ -1926,6 +1926,10 @@ ADR 0667's checks, of milestone G itself: publishes the other under `1.4.3`, which the catalog then says `version-released` once; a new `1.5` builds as `1.5.0`; two builds close together reserve two versions; a `beta` build reserves none and is delivered; a manifest saying `1.4-beta` is refused (whole, builder, indexer and Go build module, mesh-delivery and mesh-catalog). +103. **A group's run on request publishes no version** (design 47 G §373). A run on request of a group changing a + package and a module importing it builds the module against the package member's build by digest, publishes and + reserves nothing, and no `tag-wanted` is said (whole, runner, builder and Go build module, mesh-delivery and + mesh-catalog). ## What retires @@ -2021,7 +2025,7 @@ release reports the controller's code lines against research 063's baseline (acc | L6 | Provisioning leaves the controller (ADR 0570): every provision is one key of the seat `mesh-provisioner`, held by mesh-provisioner, holding a reference and never a value, and each placed module's provisioning plan a key of its state `plans`, both composed from placement, the wants and the manifests on change events; each provider acts once all a provision depends on is provided; the assigner moves a want only once its build's provisions are provided; a provision no want needs is retiring, and its account or credential is removed only on the operator's Approve, its data never; the vault mints each provision's secret sealed to the consumer's and the provider's node, and the consumer's node-engine writes it; a wait for a person is a provision's state, which the watchman reads; bus permissions stay with the grants part (ADR 0349, ADR 0230, ADR 0547 §11) | — | mesh-sdk, the node-engine (`mesh-host`), mesh-tools (the tool runner's provisioner client), mesh-catalog (mesh-provisioner, the vault, the provisioner client, postgres, keycloak, gitea, route-adapter), mesh-delivery (assigner, watchman) | 1954 | | L7 | History and records leave the controller (ADR 0571): the builder keeps its build log, one entry at a build's start and one at its end, from which build history and durations are read and the module mesh-catalog asks the builds it missed; the artifact sweep is the seat `mesh-artifact-sweep`, held by artifact-store-tools beside the artifact store, its keep set from the store's own listing, the wants and the build log, sweeping at each build's end from the lease write and recording each deletion in the hand-act log; disk readings are disk-load's own state on every node; who holds which seat is the state `seat-holders` of the seat `mesh-placement` (now `mesh-module-assigner`), and tools are found by the tool runners' announcements; what a node runs is its want and its have; the grants part writes `grants..` and keeps memberships in its own state; root-free is a probe of the self-check; no manifest but the controller's reads, invokes or consumes `mesh-controller`, but a named list kept to the lease write, so the lease write's check passes (ADR 0189, ADR 0547 §11, ADR 0549 §6) | — | mesh-sdk, mesh-controller (its pin only), mesh-delivery (builder, indexer, assigner, grants part, view), mesh-catalog (artifact-store-tools, disk-load, the backup seat's holder, mounts, nfs-server, minio, mesh-watcher, the messenger, the self-check, the module mesh-catalog, docker), mesh-tools (the tool runner) | 1955 | | B0 | The bootstrap delivery: every check, a lone pull request's as a group of one and a verdict carried after a trunk move included, is judged by a checking program built from the change's own heads in the build seat's sandbox, which also makes the heads' one seat list that the check hands the manifest validator and the composer; it brings no seat definition and no manifest field, so the running checker checks it (ADR 0644; *The bootstrap delivery, and the one milestone after it*) | 26 needs it | mesh-sdk (`go/delivery`: the seat `node-build-go`'s ask `run` and the checker's record of which checking program judged; `go/composer`: the request's seat list), mesh-catalog (mesh-build-go: the run step, the refused answer, the kept programs' tools and sweep), mesh-delivery (checker: the checking program and its command), hq (this design, the glossary) | 1921 | -| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check; and, one milestone with them, research 086's pipelines: a change of any module goes from a commit on a watched branch to the machines through its module's pipeline, its policy in its registration, ordered by edges between artifacts, with no Approve per merge and no hand act, the checker gone (ADR 0666, ADR 0667). Test counts at its start (G's prepare, 2026-11-09): mesh-delivery 665 unit and 68 integration, the node-engine (`mesh-host`) 1,742 unit; each other repository's and each catalog module's from the last check's verdict, read in the joint prepare | 26 to 62, 63 to 102 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker until its removal, builder, grants part, gate's view, forge bridge, watchman, the new pipeline runner `mesh-delivery-pipeline`, `contracts` and `sdk` moved under `modules/`), mesh-catalog (the gate, the build modules, the forge module, mesh-issues, mesh-knowledge, the `node-backup` holder, the router, nats: a module's kept data on the bus as a backed-up item and the object store beside each state), mesh-sdk (also its small object store package, for values of any size), the node-engine (`mesh-host`), hq | 1966, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | +| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check; and, one milestone with them, research 086's pipelines: a change of any module goes from a commit on a watched branch to the machines through its module's pipeline, its policy in its registration, ordered by edges between artifacts, with no Approve per merge and no hand act, the checker gone (ADR 0666, ADR 0667). Test counts at its start (G's prepare, 2026-11-09): mesh-delivery 665 unit and 68 integration, the node-engine (`mesh-host`) 1,742 unit; each other repository's and each catalog module's from the last check's verdict, read in the joint prepare | 26 to 62, 63 to 103 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker until its removal, builder, grants part, gate's view, forge bridge, watchman, the new pipeline runner `mesh-delivery-pipeline`, `contracts` and `sdk` moved under `modules/`), mesh-catalog (the gate, the build modules, the forge module, mesh-issues, mesh-knowledge, the `node-backup` holder, the router, nats: a module's kept data on the bus as a backed-up item and the object store beside each state), mesh-sdk (also its small object store package, for values of any size), the node-engine (`mesh-host`), hq | 1966, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | An issue is in one row only (ADR 0493 §2): where a row named an issue another row names too, the last row keeps it and the earlier row says so in words (the operator, 2026-10-26). Milestone M1 makes each row a record of the issue tracker diff --git a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md index 62a42ae3..82f9cf58 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md +++ b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md @@ -86,7 +86,7 @@ One file of milestone G's part of [design 47](../47-delivery-from-commit-to-deli - §64 **The run on request.** The runner's verb `run` takes one or more members, each a module at a named commit, runs them through their pipelines and delivers nothing, replacing the checker's try (ADR 0666 §18). Its callers are the sessions, as the agents' bus identities the tool runner proves, and the operator, and nothing else, since a run of a - group builds and publishes its packages' tagged versions (design 51). The runner says `run-started`, the indexer builds + group builds every member and its dependents on the build capacity (§373). The runner says `run-started`, the indexer builds the run's candidate from it, and the candidate's composition step writes no want (ADR 0656 §9); the candidate is dropped on that run's `pipeline-ended`. A step that could not run ends `could not run`, never as passed. A composition step for a node that sleeps passes it with a warning (ADR 0593 §9). diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md index bc07c3cf..bce2d577 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md @@ -198,7 +198,8 @@ events are subscribed to): a new event of the seat `knowledge-base` would be a s the merge turns, their stalled line and tools and the setting `check-gates-merge`, once their record is accepted; the last three reads of a pull request's description (ADR 0659 §4); every delivery part's call of the forge module's own tools, for the seat `git`'s verbs; and the four seat-list readers the delivery parts wire by hand. -- §321 At milestone G's delivery, from research 086 (ADR 0666, ADR 0667): the checker (`mesh-delivery-checker`), its seat, +- §321 At milestone G's delivery, from research 086 (ADR 0666, ADR 0667): the tagged versions, `tag-wanted` and + `tag-asks` (§373); the checker (`mesh-delivery-checker`), its seat, its try with `tryevent` and the indexer's try candidates, its kept verdicts and carries (the runner's from then), the checking program and `mesh/check`, and its buckets and bus account, removed with the gate's forge merge token only after the first pipeline after G's delivery has delivered a change end to end and on the operator's one word, its @@ -513,3 +514,12 @@ record it follows from, and replaces the builder's own choice where one was made without this every first version under G would start below today's and Go's `@latest` would never move (the design's choice, 2026-11-10). How it is checked: whole (indexer, builder), with `0.1.14` published before G, the first G build of `0.1` is `0.1.15`. +- §373 **A group's run on request publishes no version.** A run on request of a group builds each member that + depends on a package member against that member's build by its digest: the build module fetches the package + member's build from the store by the digest its build's done key names and lays it in the build's workspace as the + module that package names (a Go workspace's replacement, a TypeScript local package), so nothing is fetched by a + version, and no version is reserved, numbered or published for the run. The tagged versions, `tag-wanted` (the + checker's, and none of the runner's) and the builder's `tag-asks` with its tools `builder_tag`, `builder_tag_asks` + and `builder_untag` go at G's delivery (design 51, *Tagged versions*). Why: the operator, 2026-11-10 about 21:40 CET, + no custom versions for now, a tagged version being one; a run delivers nothing (ADR 0666 §18), so it needs no + version. How it is checked: test 103. diff --git a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md index 9c89370d..f33090d2 100644 --- a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md +++ b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md @@ -6,7 +6,7 @@ code: - mesh-catalog modules/artifact-store-tools (the store's tools), modules/distribution (its program, holding the two image seats) - mesh-catalog modules/gitea (package retention; the two package seats' verbs in its Go bundle) - mesh-catalog modules/mesh-build-go, modules/mesh-build-node, modules/mesh-build-image (publishing through the seats) - - mesh-delivery modules/mesh-delivery-builder (versions, tag asks; from milestone G a module's version reserved at its build's start and released by the catalog, the seat `mesh-builds`), the forge bridge, the indexer, the checker, the assigner, modules/mesh-delivery-pipeline (`tag-wanted` from milestone G) + - mesh-delivery modules/mesh-delivery-builder (versions, tag asks; from milestone G a module's version reserved at its build's start and released by the catalog, the seat `mesh-builds`), the forge bridge, the indexer, the checker, the assigner, modules/mesh-delivery-pipeline (no tagged version from milestone G) - mesh-sdk (the bus helper for sending in parts) - mesh-catalog modules/docker (image pruning) - mesh-host internal/apply (every new image of a module pulled before any of its containers is replaced; a failed pull applied again on the status view's `settled` for the store's holder, and at the node-engine's start), internal/compose (each routed image reference written with its seat and that seat's holder) @@ -345,13 +345,17 @@ the verb. ### Tagged versions +- **From the delivery of design 47's milestone G there are no tagged versions** (the operator, 2026-11-10 about 21:40 + CET: no custom versions for now; design 47 G §373). A group's run on request builds its members against each + package member's build by its digest and publishes no version; the tagged versions, the events `tag-wanted` (the + checker's and any runner's), the builder's state `tag-asks` and its tools `builder_tag`, `builder_tag_asks` and + `builder_untag` go, and nothing is numbered below a next version. The bullets below hold until that delivery. + Tagged versions already published stay until a branch's end removes them as below, and nothing new is tagged. How + it is checked: design 47's test 103. - **Asked by an event, never by a push** (ADR 0610 §1). The checker emits `tag-wanted` (repository, branch, head, tag), on a family of its own, `checker.asks`, kept seven days, apart from its verdicts, so a waiter on a verdict does not wake on it, for each head of a delivery group member that declares a package; a person asks through `builder_tag`. - From the delivery of design 47's milestone G the checker goes, and the pipeline runner says `tag-wanted` instead, on - its own seat `mesh-pipeline-runner`, for each member that declares a package of a run on request of a group not yet - merged (ADR 0666 §18): a group merged together is one source revision whose packages are published before its - dependents build, so only a run on request needs a tagged version. The + Until the delivery of design 47's milestone G (below). The builder consumes the event on a durable consumer and keeps the ask in its own state `tag-asks`, removed when its branch is merged or deleted, or its pull request closed. An ask the builder has not answered within five minutes is narrated in the build log with what it waits on. @@ -359,9 +363,8 @@ the verb. and Go's `@latest` skips it. A package new in a group is numbered from its floor (`0.1.0-feat-x.1`). - **A group's check builds after `published`**: Go members by the tagged version, TypeScript members with their lock naming it and the registry's integrity; nothing is packed for a check, and a lone pull request's check publishes - nothing. No `published` within the publish's bound (10 minutes) fails the check naming the package. From the - delivery of design 47's milestone G the same holds for a group's run on request, whose pipeline steps build after - `published`, and a step that gets no `published` within the bound ends failed, naming the package. + nothing. No `published` within the publish's bound (10 minutes) fails the check naming the package. Until the + delivery of design 47's milestone G (below). - **Removed with its branch**: on the forge bridge's `merged` event, its pull `closed`, or its branch-deleted event, heard on a durable consumer, the builder calls each seat's `remove`. It acknowledges the event only once every seat emitted `removed`; until then it retries -- 2.54.0 From b157283202be18ad3ec3a8721a854e0775ac1c34 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 21:50:26 +0100 Subject: [PATCH 06/13] Record on each released version what it was built against, and find what is behind with one query The operator chose on 2026-11-10 that the catalog of versions keeps a lockfile per released version (issue 1961): each edge's dependency version, so one query answers what is outdated after a restart or a missed event, while each edge kind is still started by its own event, a release or a delivery. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../47-delivery-from-commit-to-delivered.md | 11 ++++++- .../G-6-verbs-retires-and-answers.md | 32 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) 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 eee387ed..5c804f3b 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 @@ -1930,6 +1930,15 @@ ADR 0667's checks, of milestone G itself: package and a module importing it builds the module against the package member's build by digest, publishes and reserves nothing, and no `tag-wanted` is said (whole, runner, builder and Go build module, mesh-delivery and mesh-catalog). +104. **A release records its dependencies, and each kind follows its event** (design 47 G §374, §376). A module + released at `1.4.3`, built against mesh-sdk `0.1.15` and delivered after a provider at `2.0.1`, holds those two + versions on its release; mesh-sdk's `version-released` of `0.1.16` rebuilds it, and the provider's + `version-delivered` of `2.0.2` writes its wants again; a node away is named and waits (whole, indexer, builder, + assigner and runner, mesh-delivery). +105. **One query finds what is behind** (design 47 G §375). With the runner stopped while mesh-sdk `0.1.16` is + released, `outdated` names the module's kind-1 edge at `0.1.15` against `0.1.16`, and at its restart the runner + starts that module's pipeline once; `indexer_outdated_again` says the event again and writes the hand-act log + (whole, indexer and runner, mesh-delivery). ## What retires @@ -2025,7 +2034,7 @@ release reports the controller's code lines against research 063's baseline (acc | L6 | Provisioning leaves the controller (ADR 0570): every provision is one key of the seat `mesh-provisioner`, held by mesh-provisioner, holding a reference and never a value, and each placed module's provisioning plan a key of its state `plans`, both composed from placement, the wants and the manifests on change events; each provider acts once all a provision depends on is provided; the assigner moves a want only once its build's provisions are provided; a provision no want needs is retiring, and its account or credential is removed only on the operator's Approve, its data never; the vault mints each provision's secret sealed to the consumer's and the provider's node, and the consumer's node-engine writes it; a wait for a person is a provision's state, which the watchman reads; bus permissions stay with the grants part (ADR 0349, ADR 0230, ADR 0547 §11) | — | mesh-sdk, the node-engine (`mesh-host`), mesh-tools (the tool runner's provisioner client), mesh-catalog (mesh-provisioner, the vault, the provisioner client, postgres, keycloak, gitea, route-adapter), mesh-delivery (assigner, watchman) | 1954 | | L7 | History and records leave the controller (ADR 0571): the builder keeps its build log, one entry at a build's start and one at its end, from which build history and durations are read and the module mesh-catalog asks the builds it missed; the artifact sweep is the seat `mesh-artifact-sweep`, held by artifact-store-tools beside the artifact store, its keep set from the store's own listing, the wants and the build log, sweeping at each build's end from the lease write and recording each deletion in the hand-act log; disk readings are disk-load's own state on every node; who holds which seat is the state `seat-holders` of the seat `mesh-placement` (now `mesh-module-assigner`), and tools are found by the tool runners' announcements; what a node runs is its want and its have; the grants part writes `grants..` and keeps memberships in its own state; root-free is a probe of the self-check; no manifest but the controller's reads, invokes or consumes `mesh-controller`, but a named list kept to the lease write, so the lease write's check passes (ADR 0189, ADR 0547 §11, ADR 0549 §6) | — | mesh-sdk, mesh-controller (its pin only), mesh-delivery (builder, indexer, assigner, grants part, view), mesh-catalog (artifact-store-tools, disk-load, the backup seat's holder, mounts, nfs-server, minio, mesh-watcher, the messenger, the self-check, the module mesh-catalog, docker), mesh-tools (the tool runner) | 1955 | | B0 | The bootstrap delivery: every check, a lone pull request's as a group of one and a verdict carried after a trunk move included, is judged by a checking program built from the change's own heads in the build seat's sandbox, which also makes the heads' one seat list that the check hands the manifest validator and the composer; it brings no seat definition and no manifest field, so the running checker checks it (ADR 0644; *The bootstrap delivery, and the one milestone after it*) | 26 needs it | mesh-sdk (`go/delivery`: the seat `node-build-go`'s ask `run` and the checker's record of which checking program judged; `go/composer`: the request's seat list), mesh-catalog (mesh-build-go: the run step, the refused answer, the kept programs' tools and sweep), mesh-delivery (checker: the checking program and its command), hq (this design, the glossary) | 1921 | -| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check; and, one milestone with them, research 086's pipelines: a change of any module goes from a commit on a watched branch to the machines through its module's pipeline, its policy in its registration, ordered by edges between artifacts, with no Approve per merge and no hand act, the checker gone (ADR 0666, ADR 0667). Test counts at its start (G's prepare, 2026-11-09): mesh-delivery 665 unit and 68 integration, the node-engine (`mesh-host`) 1,742 unit; each other repository's and each catalog module's from the last check's verdict, read in the joint prepare | 26 to 62, 63 to 103 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker until its removal, builder, grants part, gate's view, forge bridge, watchman, the new pipeline runner `mesh-delivery-pipeline`, `contracts` and `sdk` moved under `modules/`), mesh-catalog (the gate, the build modules, the forge module, mesh-issues, mesh-knowledge, the `node-backup` holder, the router, nats: a module's kept data on the bus as a backed-up item and the object store beside each state), mesh-sdk (also its small object store package, for values of any size), the node-engine (`mesh-host`), hq | 1966, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | +| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check; and, one milestone with them, research 086's pipelines: a change of any module goes from a commit on a watched branch to the machines through its module's pipeline, its policy in its registration, ordered by edges between artifacts, with no Approve per merge and no hand act, the checker gone (ADR 0666, ADR 0667). Test counts at its start (G's prepare, 2026-11-09): mesh-delivery 665 unit and 68 integration, the node-engine (`mesh-host`) 1,742 unit; each other repository's and each catalog module's from the last check's verdict, read in the joint prepare | 26 to 62, 63 to 105 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker until its removal, builder, grants part, gate's view, forge bridge, watchman, the new pipeline runner `mesh-delivery-pipeline`, `contracts` and `sdk` moved under `modules/`), mesh-catalog (the gate, the build modules, the forge module, mesh-issues, mesh-knowledge, the `node-backup` holder, the router, nats: a module's kept data on the bus as a backed-up item and the object store beside each state), mesh-sdk (also its small object store package, for values of any size), the node-engine (`mesh-host`), hq | 1966, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | An issue is in one row only (ADR 0493 §2): where a row named an issue another row names too, the last row keeps it and the earlier row says so in words (the operator, 2026-10-26). Milestone M1 makes each row a record of the issue tracker diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md index bce2d577..8a89df26 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md @@ -523,3 +523,35 @@ record it follows from, and replaces the builder's own choice where one was made and `builder_untag` go at G's delivery (design 51, *Tagged versions*). Why: the operator, 2026-11-10 about 21:40 CET, no custom versions for now, a tagged version being one; a run delivers nothing (ADR 0666 §18), so it needs no version. How it is checked: test 103. +- §374 **A released version records the versions it was built against or delivered after.** When the catalog + records a module's version as released (§349), it writes on that version, in its key of `versions`, one entry per + catalog-graph edge of the module's artifacts (the six kinds of *Dependencies between artifacts*): the kind, the + dependency's artifact reference and the dependency's version, a lockfile per released version. For the build kinds + (1 and 3) the version is the one the build used: the build module names each dependency version it resolved in its + step key, and the builder carries them in each artifact's `package-published`, so the indexer reads no other part's + state (ADR 0596). For the delivery kinds (2, 4, 5 and 6) it is the dependency's newest version running when the + released version first runs on a node, heard from the node-engine seat's `running` events through a durable consumer + of the indexer's own, the fingerprint mapped to its version in `versions`. The declared edge stays in the catalog + graph; the version is the release's fact, never changed after. Why: the operator, 2026-11-10 about 22:10 CET (issue + 1961). Each entry is about 0.1 KiB, about ten a version, so `versions` stays within its 64 MiB (an estimate). How it + is checked: test 104. +- §375 **One query finds every edge behind.** The seat `mesh-catalog`'s verb `outdated`, with the indexer's tool + `indexer_outdated`, answers every recorded edge whose version is lower than the dependency's newest released + version (kinds 1 and 2) or newest delivered version (kinds 3 to 6): the module and its version, the edge's kind, the + dependency, the recorded and the newest version, and the act that catches it up. A delivered version is one the + catalog has said `version-delivered` for (§376). Its callers are any person, an agent session, the healers, the + runner and the assigner; `indexer_outdated_again`, a hand act written in the hand-act log, says the event that + starts an edge's work again for one dependency. At its start, the runner and the assigner each ask `outdated` once + and start the work it names, so a restart or a missed event leaves nothing behind. Why: the operator, 2026-11-10 + (issue 1961's "outdated"); events start the work, and the query finds what is behind and answers a person. How it is + checked: test 105. +- §376 **Each kind is checked on its own event.** After a part's version is released, on its `version-released`: + the dependents by kind 1 are rebuilt against it and their pipelines started, as on `package-published` (§53), and + those by kind 2 are delivered again, the assigner writing their wants again naming the new version. After a part is + delivered, on the catalog's `version-delivered` (the module and the version, said once every present node that + places it runs it healthy, from the node-engine seat's `running`; nodes away are named and wait, ADR 0603 §7): the + dependents by kind 3 are rebuilt and their pipelines started, and those by kinds 4 and 6 are delivered again by + their wants. Kind 5, an acting part's act for a new version, is held by the acting parts' waits (*Within a revision + the new version acts first*) and is only recorded and shown by `outdated`. `version-delivered` is said on + `mesh.seat.mesh-catalog.event.version-delivered`, kept 90 days at 16 MiB. Why: the operator, 2026-11-10; the + events still start the work (ADR 0666 §14, §15). How it is checked: tests 104 and 105. -- 2.54.0 From 5df2eca59e76c4266c269332d1a9f21ee7f87a07 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 21:52:18 +0100 Subject: [PATCH 07/13] Compare a release's edges by the dependency part's digest and per node, and fill them once at the start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A module's version can change without changing the part an edge points to, a delivery happens per node, a build of another branch has no version, and acting and serving are facts, not versions: the lead's review of §374 to §376 found each would mark edges behind wrongly or redo work. A first fill keeps the first query from finding the whole mesh behind. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../47-delivery-from-commit-to-delivered.md | 13 ++- .../G-6-verbs-retires-and-answers.md | 92 ++++++++++++------- 2 files changed, 68 insertions(+), 37 deletions(-) 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 5c804f3b..33dbbbb4 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 @@ -1931,14 +1931,17 @@ ADR 0667's checks, of milestone G itself: reserves nothing, and no `tag-wanted` is said (whole, runner, builder and Go build module, mesh-delivery and mesh-catalog). 104. **A release records its dependencies, and each kind follows its event** (design 47 G §374, §376). A module - released at `1.4.3`, built against mesh-sdk `0.1.15` and delivered after a provider at `2.0.1`, holds those two - versions on its release; mesh-sdk's `version-released` of `0.1.16` rebuilds it, and the provider's - `version-delivered` of `2.0.2` writes its wants again; a node away is named and waits (whole, indexer, builder, - assigner and runner, mesh-delivery). + released at `1.4.3`, built against mesh-sdk `0.1.15` and delivered after a provider at `2.0.1`, holds those + versions with their artifacts' digests, the provider's per node; mesh-sdk's `version-released` of `0.1.16` that + changes the imported package rebuilds it, and one that leaves its digest as it was rebuilds nothing; the + provider's new build running on one node writes the module's want again on that node only, while another node + waits for its own; a `beta` build's entries compare by digest (whole, indexer, builder, assigner and runner, + mesh-delivery). 105. **One query finds what is behind** (design 47 G §375). With the runner stopped while mesh-sdk `0.1.16` is released, `outdated` names the module's kind-1 edge at `0.1.15` against `0.1.16`, and at its restart the runner starts that module's pipeline once; `indexer_outdated_again` says the event again and writes the hand-act log - (whole, indexer and runner, mesh-delivery). + (whole, indexer and runner, mesh-delivery). After the first fill (§377), a mesh running its newest builds is + answered with nothing behind. ## What retires diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md index 8a89df26..5a500219 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md @@ -523,35 +523,63 @@ record it follows from, and replaces the builder's own choice where one was made and `builder_untag` go at G's delivery (design 51, *Tagged versions*). Why: the operator, 2026-11-10 about 21:40 CET, no custom versions for now, a tagged version being one; a run delivers nothing (ADR 0666 §18), so it needs no version. How it is checked: test 103. -- §374 **A released version records the versions it was built against or delivered after.** When the catalog - records a module's version as released (§349), it writes on that version, in its key of `versions`, one entry per - catalog-graph edge of the module's artifacts (the six kinds of *Dependencies between artifacts*): the kind, the - dependency's artifact reference and the dependency's version, a lockfile per released version. For the build kinds - (1 and 3) the version is the one the build used: the build module names each dependency version it resolved in its - step key, and the builder carries them in each artifact's `package-published`, so the indexer reads no other part's - state (ADR 0596). For the delivery kinds (2, 4, 5 and 6) it is the dependency's newest version running when the - released version first runs on a node, heard from the node-engine seat's `running` events through a durable consumer - of the indexer's own, the fingerprint mapped to its version in `versions`. The declared edge stays in the catalog - graph; the version is the release's fact, never changed after. Why: the operator, 2026-11-10 about 22:10 CET (issue - 1961). Each entry is about 0.1 KiB, about ten a version, so `versions` stays within its 64 MiB (an estimate). How it - is checked: test 104. -- §375 **One query finds every edge behind.** The seat `mesh-catalog`'s verb `outdated`, with the indexer's tool - `indexer_outdated`, answers every recorded edge whose version is lower than the dependency's newest released - version (kinds 1 and 2) or newest delivered version (kinds 3 to 6): the module and its version, the edge's kind, the - dependency, the recorded and the newest version, and the act that catches it up. A delivered version is one the - catalog has said `version-delivered` for (§376). Its callers are any person, an agent session, the healers, the - runner and the assigner; `indexer_outdated_again`, a hand act written in the hand-act log, says the event that - starts an edge's work again for one dependency. At its start, the runner and the assigner each ask `outdated` once - and start the work it names, so a restart or a missed event leaves nothing behind. Why: the operator, 2026-11-10 - (issue 1961's "outdated"); events start the work, and the query finds what is behind and answers a person. How it is - checked: test 105. -- §376 **Each kind is checked on its own event.** After a part's version is released, on its `version-released`: - the dependents by kind 1 are rebuilt against it and their pipelines started, as on `package-published` (§53), and - those by kind 2 are delivered again, the assigner writing their wants again naming the new version. After a part is - delivered, on the catalog's `version-delivered` (the module and the version, said once every present node that - places it runs it healthy, from the node-engine seat's `running`; nodes away are named and wait, ADR 0603 §7): the - dependents by kind 3 are rebuilt and their pipelines started, and those by kinds 4 and 6 are delivered again by - their wants. Kind 5, an acting part's act for a new version, is held by the acting parts' waits (*Within a revision - the new version acts first*) and is only recorded and shown by `outdated`. `version-delivered` is said on - `mesh.seat.mesh-catalog.event.version-delivered`, kept 90 days at 16 MiB. Why: the operator, 2026-11-10; the - events still start the work (ADR 0666 §14, §15). How it is checked: tests 104 and 105. +- §374 **A release records, per edge, the part it was built against or delivered after.** The catalog records on + each released version (§349), in its key of `versions`, one entry per catalog-graph edge of the module's artifacts + (the six kinds of *Dependencies between artifacts*), a lockfile per released version. An entry names the kind, the + dependency's artifact reference and what the edge depended on: + - kinds 1 and 3 (built against): the dependency's version and the digest of that artifact, as the build used them. + The build module names each dependency it resolved, version and digest, in its step key, and the builder carries + them in each artifact's `package-published`, so the indexer reads no other part's state (ADR 0596). A build is on + one build node, so these entries are per release. + - kinds 2 and 4 (delivered after): per node, the dependency's version and artifact digest running on that node + when the released version first runs there, heard from the node-engine seat's `running` events through a durable + consumer of the indexer's own, the fingerprint mapped to its version and digest in `versions`. + - kind 5 (has acted for): a fact, not a version: the acting part, its step, the build that acted and that the act + was done (or released by hand), from the holder's `wait-ended` (*Each step's wait is its own part's*). + - kind 6 (runs a version that serves): a fact, per node: the verb or manifest word used and the dependency's build + on that node that serves it, from the node-engine seat's `running` and the catalog entry of that build. + + A build of a watched branch other than the registration's first releases no version (§371): its entries are kept in + the indexer's old `state` section `build-edges`, one key per module and watched branch holding the newest build's + entries, keyed by its fingerprint, replaced by the next build and deleted when `registration-changed` says the + branch is no longer watched. The declared edge stays in the catalog graph; an entry is the release's or the build's + fact, never changed after. Why: the operator, 2026-11-10 about 22:10 CET (issue 1961); a version is per module and an + edge per artifact, so the artifact's digest is what an edge compares (the lead's review of these lines, same + evening). Each entry is about 0.1 KiB, about ten a version and four nodes for the delivery kinds, so `versions` + stays within its 64 MiB (an estimate). How it is checked: test 104. +- §375 **One query finds every edge behind, by the part's digest.** The seat `mesh-catalog`'s verb `outdated`, with + the indexer's tool `indexer_outdated`, answers every entry that is behind: for kinds 1 and 2, the recorded artifact + digest differs from that artifact's digest in the dependency's newest released version (for a build of another + branch, its newest build on that branch); for kinds 3 and 4, it differs from the digest the dependency's newest build + has where it acts (for kind 3 the agreed toolchain, for kind 4 what runs on that node); for kind 5, the newest version + of the acting part has not acted for the dependent's current build; for kind 6, the build on that node no longer + serves the verb or word. A dependency's newer version that left the artifact's digest as it was makes nothing behind, + so nothing is done twice for the same inputs. Each answer names the module and its version or build, the node for a + per-node entry, the kind, the dependency, what was recorded and what is newest, and the act that catches it up. Its + callers are any person, an agent session, the healers, the runner and the assigner; `indexer_outdated_again`, a hand + act written in the hand-act log, says the event that starts an entry's work again. At its start, the runner and the + assigner each ask `outdated` once and start the work it names, so a restart or a missed event leaves nothing behind. + Why: the operator, 2026-11-10 (issue 1961's "outdated"); events start the work, and the query finds what is behind + and answers a person. How it is checked: test 105. +- §376 **Each kind is checked on its own event, per node where it is delivered.** After a dependency's version is + released, on its `version-released`: the dependents by kind 1 whose entry is behind are rebuilt against it and their + pipelines started, as on `package-published` (§53), and those by kind 2 are delivered again on each node whose entry + is behind, the assigner writing that node's want again. After a dependency is delivered: by kind 3, when the agreed + toolchain names the build module's new build (*A build module's wait ends on the agreed toolchain*), its dependents + whose entry is behind are rebuilt; by kinds 4 and 6, on the node-engine seat's `running..` of the + dependency's new build on a node, the dependents on that node whose entry is behind are delivered again by their + wants there, so a dependent on one node follows its own node and waits for no other. The catalog also says + `version-delivered` (the module and the version, once every present node that places it runs it healthy, nodes away + named, ADR 0603 §7) on `mesh.seat.mesh-catalog.event.version-delivered`, kept 90 days at 16 MiB: the mesh-wide summary + a person and the status view read, which starts no per-node work. Kind 5 is held by the acting parts' waits + (*Within a revision the new version acts first*) and only shown by `outdated`. Why: the operator, 2026-11-10; the + events still start the work (ADR 0666 §14, §15); per node, since a node away holds only itself (ADR 0603 §7). How it + is checked: tests 104 and 105. +- §377 **The first fill.** At the indexer's first start under G, after `versions` is filled (§372), each module's + newest version gets its entries once: the build kinds from the dependency version published latest before that + version's build was claimed, read with the times of `builder_versions`, and its digest; the delivery kinds, per + node, from the last `running` of each module on each node, which the node-engine seat's stream keeps last per + subject with no age limit; kind 5 as done for every acting part's running build; kind 6 from what each node runs. + Each filled entry is marked `filled`, so a person sees it was derived, not recorded. It is not filled again. Why: so + the first `outdated` does not find the whole mesh behind (the lead, 2026-11-10). How it is checked: whole + (indexer), after the first fill `outdated` answers nothing for a mesh whose modules run their newest builds. -- 2.54.0 From 893d83d23bbf3060499b8cf93eaf79371cbe3ce7 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 22:22:14 +0100 Subject: [PATCH 08/13] Rebuild only on the edges a part is made from, wait for a delivery mesh-wide, and name the version in each want The operator answered on 2026-11-10 that only an imported package and the build toolchain make a dependent stale, every other edge only ordering, that a dependent waits for its dependency's delivery to every present machine, that a want names the released version and each part's digest, and that one tool answers a part's edges both ways. G's answers move into a file of their own so each file of the part stays under 60 KB. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../47-delivery-from-commit-to-delivered.md | 23 +- .../G-5-acting-parts-check-and-kept-data.md | 2 +- .../47-milestones/G-6-verbs-and-retires.md | 204 ++++++++++++ ...-retires-and-answers.md => G-7-answers.md} | 309 ++++-------------- 03-DESIGN/01-to-be/47-milestones/G.md | 5 +- 5 files changed, 295 insertions(+), 248 deletions(-) create mode 100644 03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md rename 03-DESIGN/01-to-be/47-milestones/{G-6-verbs-retires-and-answers.md => G-7-answers.md} (56%) 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 33dbbbb4..dbe070ab 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 @@ -514,7 +514,9 @@ per module and node from the keys: provision `bus-state` holds no requirer for it (ADR 0614 §5, ADR 0429 §6). The rule reads what runs, never the phase of a want not yet applied: on 2026-11-09 a yes not yet asked on the bus held the delivery of every module asking bus state (issue 1966, note 150). How it is checked: whole (assigner), a provider whose new want waits for a yes, with its old - build running healthy, holds no consumer's want, and one whose running build is unhealthy holds it. + build running healthy, holds no consumer's want, and one whose running build is unhealthy holds it. From G's + delivery a new version ordered after its dependency's new version also waits until that version is delivered to + every present machine placing it (design 47 G §376). - **The canary before the rest.** A new fingerprint goes to the module's canary first, and to its other nodes once it runs healthy there, at most `step` of them ahead when `step` is above zero (rules 3 and 4). - **A credential and a composition before the want.** The module's credential and grant are written when its @@ -1930,18 +1932,23 @@ ADR 0667's checks, of milestone G itself: package and a module importing it builds the module against the package member's build by digest, publishes and reserves nothing, and no `tag-wanted` is said (whole, runner, builder and Go build module, mesh-delivery and mesh-catalog). -104. **A release records its dependencies, and each kind follows its event** (design 47 G §374, §376). A module - released at `1.4.3`, built against mesh-sdk `0.1.15` and delivered after a provider at `2.0.1`, holds those - versions with their artifacts' digests, the provider's per node; mesh-sdk's `version-released` of `0.1.16` that - changes the imported package rebuilds it, and one that leaves its digest as it was rebuilds nothing; the - provider's new build running on one node writes the module's want again on that node only, while another node - waits for its own; a `beta` build's entries compare by digest (whole, indexer, builder, assigner and runner, +104. **A release records its dependencies, and only a stale edge rebuilds** (design 47 G §374, §376, §378). A + module released at `1.4.3`, built against mesh-sdk `0.1.15` and delivered after a provider at `2.0.1`, holds + those versions with their parts' digests; mesh-sdk's `version-released` of `0.1.16` that changes the imported + package rebuilds it, one that leaves its digest as it was rebuilds nothing, and a change of the Go build module's + own code rebuilds no module; a `beta` build's entries compare by digest (whole, indexer, builder and runner, mesh-delivery). 105. **One query finds what is behind** (design 47 G §375). With the runner stopped while mesh-sdk `0.1.16` is released, `outdated` names the module's kind-1 edge at `0.1.15` against `0.1.16`, and at its restart the runner starts that module's pipeline once; `indexer_outdated_again` says the event again and writes the hand-act log (whole, indexer and runner, mesh-delivery). After the first fill (§377), a mesh running its newest builds is answered with nothing behind. +106. **A delivery is waited for mesh-wide** (design 47 G §376, §379, §380). A module ordered after a provider's + new version is wanted only once `version-delivered` says every present machine placing the provider runs it; with + one machine away the module's status reads "waiting for delivery of" the provider, naming the machine, and it + catches up when that machine is back; with every placing machine away the provider reads "waiting for + to be back" and nothing is raised; each want names the released version and its parts' digests; `indexer_edges` + answers the module's edges from both ends (whole, indexer, assigner and node-engine, mesh-delivery and `mesh-host`). ## What retires @@ -2037,7 +2044,7 @@ release reports the controller's code lines against research 063's baseline (acc | L6 | Provisioning leaves the controller (ADR 0570): every provision is one key of the seat `mesh-provisioner`, held by mesh-provisioner, holding a reference and never a value, and each placed module's provisioning plan a key of its state `plans`, both composed from placement, the wants and the manifests on change events; each provider acts once all a provision depends on is provided; the assigner moves a want only once its build's provisions are provided; a provision no want needs is retiring, and its account or credential is removed only on the operator's Approve, its data never; the vault mints each provision's secret sealed to the consumer's and the provider's node, and the consumer's node-engine writes it; a wait for a person is a provision's state, which the watchman reads; bus permissions stay with the grants part (ADR 0349, ADR 0230, ADR 0547 §11) | — | mesh-sdk, the node-engine (`mesh-host`), mesh-tools (the tool runner's provisioner client), mesh-catalog (mesh-provisioner, the vault, the provisioner client, postgres, keycloak, gitea, route-adapter), mesh-delivery (assigner, watchman) | 1954 | | L7 | History and records leave the controller (ADR 0571): the builder keeps its build log, one entry at a build's start and one at its end, from which build history and durations are read and the module mesh-catalog asks the builds it missed; the artifact sweep is the seat `mesh-artifact-sweep`, held by artifact-store-tools beside the artifact store, its keep set from the store's own listing, the wants and the build log, sweeping at each build's end from the lease write and recording each deletion in the hand-act log; disk readings are disk-load's own state on every node; who holds which seat is the state `seat-holders` of the seat `mesh-placement` (now `mesh-module-assigner`), and tools are found by the tool runners' announcements; what a node runs is its want and its have; the grants part writes `grants..` and keeps memberships in its own state; root-free is a probe of the self-check; no manifest but the controller's reads, invokes or consumes `mesh-controller`, but a named list kept to the lease write, so the lease write's check passes (ADR 0189, ADR 0547 §11, ADR 0549 §6) | — | mesh-sdk, mesh-controller (its pin only), mesh-delivery (builder, indexer, assigner, grants part, view), mesh-catalog (artifact-store-tools, disk-load, the backup seat's holder, mounts, nfs-server, minio, mesh-watcher, the messenger, the self-check, the module mesh-catalog, docker), mesh-tools (the tool runner) | 1955 | | B0 | The bootstrap delivery: every check, a lone pull request's as a group of one and a verdict carried after a trunk move included, is judged by a checking program built from the change's own heads in the build seat's sandbox, which also makes the heads' one seat list that the check hands the manifest validator and the composer; it brings no seat definition and no manifest field, so the running checker checks it (ADR 0644; *The bootstrap delivery, and the one milestone after it*) | 26 needs it | mesh-sdk (`go/delivery`: the seat `node-build-go`'s ask `run` and the checker's record of which checking program judged; `go/composer`: the request's seat list), mesh-catalog (mesh-build-go: the run step, the refused answer, the kept programs' tools and sweep), mesh-delivery (checker: the checking program and its command), hq (this design, the glossary) | 1921 | -| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check; and, one milestone with them, research 086's pipelines: a change of any module goes from a commit on a watched branch to the machines through its module's pipeline, its policy in its registration, ordered by edges between artifacts, with no Approve per merge and no hand act, the checker gone (ADR 0666, ADR 0667). Test counts at its start (G's prepare, 2026-11-09): mesh-delivery 665 unit and 68 integration, the node-engine (`mesh-host`) 1,742 unit; each other repository's and each catalog module's from the last check's verdict, read in the joint prepare | 26 to 62, 63 to 105 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker until its removal, builder, grants part, gate's view, forge bridge, watchman, the new pipeline runner `mesh-delivery-pipeline`, `contracts` and `sdk` moved under `modules/`), mesh-catalog (the gate, the build modules, the forge module, mesh-issues, mesh-knowledge, the `node-backup` holder, the router, nats: a module's kept data on the bus as a backed-up item and the object store beside each state), mesh-sdk (also its small object store package, for values of any size), the node-engine (`mesh-host`), hq | 1966, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | +| G | Two graphs and one revision view: the catalog graph and the assignment graph kept per revision, a change delivered as its graph difference, the acting parts' rules on their seats as bounded waits that hold their modules when the new version fails, the check on the change's own acting parts, the revision view and each process's own build, the gate's delivery record, and the trace's links (ADR 0656); no part reads a pull request's description (ADR 0659); every delivery part reaches the forge through the seat `git` and names no other module; nothing holds an approved merge for another check; and, one milestone with them, research 086's pipelines: a change of any module goes from a commit on a watched branch to the machines through its module's pipeline, its policy in its registration, ordered by edges between artifacts, with no Approve per merge and no hand act, the checker gone (ADR 0666, ADR 0667). Test counts at its start (G's prepare, 2026-11-09): mesh-delivery 665 unit and 68 integration, the node-engine (`mesh-host`) 1,742 unit; each other repository's and each catalog module's from the last check's verdict, read in the joint prepare | 26 to 62, 63 to 106 | mesh-sdk (the runtime: the fingerprint, the view, the definition reader, the seat list's one wiring), mesh-contracts and the domains' contracts modules (`acts`, the new verbs and events, the seat `mesh-grants`, the seat `git`'s verbs), mesh-delivery (indexer, assigner, checker until its removal, builder, grants part, gate's view, forge bridge, watchman, the new pipeline runner `mesh-delivery-pipeline`, `contracts` and `sdk` moved under `modules/`), mesh-catalog (the gate, the build modules, the forge module, mesh-issues, mesh-knowledge, the `node-backup` holder, the router, nats: a module's kept data on the bus as a backed-up item and the object store beside each state), mesh-sdk (also its small object store package, for values of any size), the node-engine (`mesh-host`), hq | 1966, 1961 (retargeted to this row's shape), 1660, 1972, 1721, 1783, 1785, 1794, 1791, 1373, 595; 1962 goes back with 1958, whose assignment entry it needs (ADR 0641 §3) | An issue is in one row only (ADR 0493 §2): where a row named an issue another row names too, the last row keeps it and the earlier row says so in words (the operator, 2026-10-26). Milestone M1 makes each row a record of the issue tracker diff --git a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md index 5eab93ae..2c767215 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md +++ b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md @@ -73,7 +73,7 @@ names go, since every new acting part needed code changes elsewhere (research 08 Every other step's wait ends when the acting part's new version runs healthy, as the node-engine seat's `running` event says. - §268 **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 + another node's, so a node away holds only itself (ADR 0603 §7). The want gains no field for it: the assigner, which writes the wants, holds a node's other wants until the node-engine seat's `running..` 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 diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md new file mode 100644 index 00000000..fc13b0f3 --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md @@ -0,0 +1,204 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-10 +decisions: + - "ADR 0667" + - "ADR 0666" + - "ADR 0660" + - "ADR 0659" + - "ADR 0656" + - "ADR 0625" + - "ADR 0614" + - "ADR 0603" + - "ADR 0596" + - "ADR 0551" + - "ADR 0548" + - "ADR 0534" + - "ADR 0493" + - "ADR 0443" + - "ADR 0441" + - "ADR 0398" +--- + +# 47, milestone G: the verbs G adds and what retires + +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". + +## The verbs milestone G adds + +§304 From research 085's milestone the seat gains: + +| Verb or event | Answers or does | +|---|---| +| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656), and the acts the gate said it owes the forge for it, each with its id and its recorded outcome (*What the gate owes the forge*) | +| `owed` | an act the gate owes the forge (a merge, a status, the delivery's view, a comment, a line on the commit's note), with its pull request, head and id, on `mesh.seat.mesh-delivery.event.owed..`, said only from the gate's account and heard by the forge bridge through a durable consumer (*What the gate owes the forge*) | +| `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | until G's delivery: the one Approve ask per milestone (ADR 0493 §9 as ADR 0659 §1 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.`, the milestone in the 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`. G's delivery ends the ask and its three events with the Approve per merge (ADR 0666 §10) | +| `record-opened` | a delivery record opened, which the watchman times for `record-open` | +| `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`, `room` | a delivery record ended; the ledger past 80 % of its cap; the ledger full; the ledger back below its cap (*The delivery record*) | + +§305 From milestone G's delivery the seat's `table`, `recheck` and `close` go with the merge states (*The state table*), +the milestone ask and its three events with the Approve per merge, and it gains (ADR 0666 §6, §26): + +| Verb or event | Answers or does | +|---|---| +| `source` with a watched branch | that branch's source revision; the indexer and the node-engine's catch-up read it, never the gate's state | +| `source-moved` | a new source revision of a watched branch | +| `recovery` | marks a merge its author names a recovery (the commit and why), its record marked `recovery` with the caller the runtime proves | +| `record-opened`, `recovery-merged` | a delivery record opened; a merge named a recovery, which the teller passes on to every channel | + +§306 **The seat `mesh-pipeline-runner`**, from milestone G's delivery, defined in `mesh-delivery-contracts` with the callers +of each verb (ADR 0666 §7, §20, §25): + +| Verb | Answers or does | Callers | +|---|---|---| +| `run` | the run on request of one or more members, each a module at a named commit; delivers nothing | the sessions (the agents' bus identities the tool runner proves) and the operator, nothing else | +| `start` | a delivering pipeline for a watched branch's head, by hand | the operator, the healers | +| `show`, `list`, `stands`, `why-carried`, `kept` | one pipeline whole with its step log; the pipelines and runs of a module, a branch or a commit; what stands still; where a result was carried from; the kept results | any person, an agent session, the healers | +| `review` | a code review's result for a commit and fingerprint | the reviewers the definition names; refused from the commit's author | +| `rerun`, `accept`, `cancel`, `ask-again` | one step again; a failed `optional` step accepted with why; a pipeline ended, its asks dropped; a declined `approval` asked again | the operator, the healers (`accept` and `ask-again` the operator) | + +§307 **The seat `mesh-catalog`** gains, from milestone G's delivery, `register`, `policy`, `watch`, `unwatch` and +`registrations`, and the event `registration-changed` (*A module's registration*), and `catalog-revision` names its +watched branch in its subject. **The build seats** gain the verb +`steps` and the accepted kind `step`, and **the seat `mesh-wants`** the kind `step`, each saying `step-ended`. **The +seat `mesh-builds`**, new and held once for the mesh, says `package-published` (*A publish rebuilds*). + +§308 **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 +each step says*. + +§309 **The seat `node-engine`**'s event `running..` carries, from research 085's milestone, the module, its +fingerprint and the digest of its build's manifest, so the node-engine of another node and the grants part decide a +seat's pass from it (ADR 0660 §2; *Who holds a seat is applied by one rule*). No verb is added. + +§310 **The seat `git`.** The seat `git` (design 26, *The git seat*), of the Delivery domain, defined in +`mesh-delivery-contracts`, holds no verb today, and the delivery parts and the gate reach the forge by naming the forge +module's own tools and events and guessing the shape of their answers (on 2026-11-09 one guess, an answer wrapped in a +`result` field, made 316 conditions stand, issue 1966). From research 085's milestone the seat's definition carries the +verbs and events below, their inputs and answers typed in its contract, served and said by the module that claims the +seat; every delivery part and the gate call and hear the seat, never the forge module by name (acceptance test 27). +They are this design's choice under ADR 0596 §1, §2 (a module's events are subscribed to and its data asked through +its seat verbs) and ADR 0625 §1 (a seat's contract is generated from its definition). The table is built from what the +parts' manifests declare in `invokes` and `consumes` and what their code calls, read on main on 2026-11-09 (mesh-delivery +042b8d1f, mesh-catalog's gate); a declaration nothing calls (the checker's `gitea_commits`, `gitea_compare`, +`gitea_list_branches`) gets no verb and is dropped from its manifest: + +| Verb | Answers or does | Called by (today's call) | +|---|---|---| +| `pull` | one pull request, by its repository and number or by its merge commit: number, base, head branch, head, merge commit, author, state, and `merging` while the forge merges it; an unknown one answers the error kind `not-found` | the forge bridge (a merge the gate did not make; `gitea_api`), the gate (a stalled line's author) | +| `branch` | from milestone G's delivery: a branch's head, and whether a protection rule refuses a direct push and admits only merges | the indexer (`register`, `watch`), the forge bridge (a watched branch's head) | +| `open-pulls` | every open pull request of every repository, its head and base, the statuses its base requires and those its head has | the forge bridge at its start and each reconnect (ADR 0443 §5), the indexer, the gate (`gitea_open_pulls`) | +| `tags` | a repository's tags, paged, the page's bound said when more exist | the forge bridge, for the builder's floor (`gitea_api`) | +| `trees` | the trees of one commit | the forge bridge, the checker, the indexer (`gitea_commit_trees`) | +| `file` | one file at one commit | the forge bridge, the checker, the indexer (`gitea_get_file`) | +| `contains` | whether one commit contains another | the indexer, the gate (`gitea_contains`) | +| `statuses` | the statuses a commit carries | the gate (`gitea_commit_statuses`) | +| `status`, `comment`, `view`, `note` | a commit status, a comment, the delivery's one view comment, a line on the commit's note under `refs/notes/mesh-plan`; a comment and a note line carry the act's id and an id already held is skipped | the forge bridge alone, performing the gate's and the checker's `owed` (`gitea_commit_status`, `gitea_add_comment`, `gitea_delivery_view`, `gitea_note_append`) | +| `merge-pull` | until G's delivery: merges a pull request at the head the operator's Approve names; refuses a head that moved, a branch no rule guards, a head the Approve does not name and a merge git refuses; judges no check (below). From G's delivery: merges with the forge tools' account (ADR 0398 §2) at the head the caller names, judging that it is still the pull request's head and that the branch's protection refuses a direct push, the protection read again on every `branch-moved`, and refusing a head when any run at its build-input fingerprint failed a `required` step, judged from its own state `failed-steps` heard from the runner's `step-ended`, never by a call; a head with no run yet or a passed one merges, it never waits for a run and never asks an Approve (ADR 0666 §11). A pull request already merged at that head is answered performed | until G's delivery the forge bridge alone (`gitea_merge_pull_request`); from it any session the tool runner proves, and the operator | + +| Event of the seat | Says | Heard by (today's event) | +|---|---|---| +| `pull.updated`, `pull.merged`, `pull.closed` | a pull request's new head, its merge with the merge commit, its close | the forge bridge, the gate (`gitea.pull.*`) | +| `forge.pull`, `forge.status`, `forge.delete` | the forge's own webhook of a pull request, of a commit status, of a deleted branch | the gate (`forge.pull`, `forge.status`), the forge bridge (`forge.delete`) (`gitea.forge.*`) | +| `repo.writable` | a repository the forge can write again | the gate (`gitea.repo.writable`) | +| `branch-moved` | from milestone G's delivery: a branch's new head, by a merge or an admitted push, from the forge's push webhook | the forge bridge | + +§311 Each is said on `mesh.seat.git.event.`, declared in the seat's definition, and heard through the hearer's +`consumes` of the seat. + +- §312 **A failed read answers an error, never an empty answer.** When the forge does not answer, refuses or answers what + the contract cannot type, the verb answers an error naming why; an empty list or an absent file is answered only + when the forge said so. A caller never reads an error as "none". How it is checked: the seat's fixtures hold an + error answer for each verb, and each caller's unit table has a row for it (the forge bridge, the checker, the + indexer, the gate). +- §313 **Who may call an act.** The holder's trusted config value `callers`, which today names per verb who may call the + two package seats' verbs (design 51), gains a line per verb of the seat `git`: the act verbs name the forge bridge + alone, the reads name each part that declares them in `invokes`; the grants part grants by it, and the holder + refuses a caller it does not name. From G's delivery the callers per verb are the seat definition's field `callers`, + the grants part's one source (ADR 0666 §20): `status`, `comment`, `view` and `note` the forge bridge alone, + `merge-pull` the persons and agent sessions the tool runner proves, the reads the parts that declare them. The forge module's own acting tools (`gitea_merge_pull_request`, + `gitea_commit_status`, `gitea_delivery_view`, `gitea_note_append`, `gitea_add_comment`) retire with the milestone, + and so do its watch of the checker's state `owed` and its read of the gate's `source`, so the bridge stays the one + part that acts on the forge. How it is checked: test 51 reads the grants, not the code. +- §314 **`merge-pull` judges no check, and from G's delivery no Approve, only the runs at the head's fingerprint.** The operator's answer that no check gates delivery (Telegram, 2026-11-09 01:11 UTC, + "Remove them in G") covers the forge module's own setting `check-gates-merge` as well as the gate's: both are + removed, and `merge-pull` merges an approved head whatever its `mesh/check` says. The record that ends ADR 0551 and + ADR 0548 §2 to §4, §9 and §10 also changes ADR 0534 §4's judgement of `mesh/check` by the merge; it is to be accepted + before the push (*Nothing merges into a repository under check*). From G's delivery it judges no Approve either, its caller + is any session the tool runner proves, and it refuses a head when any run at its fingerprint failed a `required` step, + never waiting for one (ADR 0666 §11; *Approval is a step, and a session merges a pull request*). The reads the + gate would have needed to merge (the forge's word for a conflict, the base's required statuses, the merging account's + write access) are not added: no part of the mesh but `merge-pull` merges, and it answers git's own refusal of a merge + as refused. +- §315 **The order is the module graph's, and each step has its way back.** The verbs are a served-verb edge (ADR 0614): + the forge module's new build runs before the forge bridge's, and the forge bridge's before the gate's, since the + bridge calls the verbs and performs the gate's `owed`, and the gate says `owed` only to a bridge that hears it; all + three are ahead of the acting parts' steps, which read through the seat. While the new forge runs and the old bridge + still calls the retired acting tools, the old bridge's acts fail loudly and stay unperformed in the old gate's keys, + and the switch carries them (*What the gate owes the forge*). A revert goes back in the other order: the gate, then + the bridge, then the forge, each wanted only once the one after it in the forward order runs its reverted build, so + no part calls a verb nobody serves. ADR 0656 §3 stands beside it: the acting parts' new versions act first in their + own steps. Two things cover the gap while the forge is still on its old build and serves no verb of the seat: the + indexer starts on its kept definitions (below), and a check whose read through the seat `git` fails says `could not + run`, naming the forge, and is asked again on the forge module's `running` (*A check runs to its end*). +- §316 **A part that cannot read the forge keeps what it has, and says so.** The indexer, at its start, waits for the + seat `git` at most 2 minutes; with no answer it judges from the last definitions it kept, says in its + `_first_views` that it waits for `git` and since when, writes `view-waits` after five minutes, and reads the + definitions once the seat answers; it then judges again every manifest it judged from kept definitions. Two minutes is longer than a forge's restart, and short enough that a start is + not held behind a forge down for good. How it is checked: test 54. +- §317 **How it is checked.** The seat definition's conformance fixtures hold one answer and one error of each verb and one + of each event, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's, the + indexer's and the gate's unit fakes answer from the same fixtures; a call of an act verb from an account other than + the forge bridge's is refused (test 51); live, a search of the delivery parts' and the gate's code finds no name of + the forge module's tools or events. + +§318 **Links of the trace.** No part reads a review line (ADR 0659 §1 to §3); who may review and the review verb's grant +stay issue 1782's, outside milestone G, and that verb gates nothing (ADR 0659 §2). The trace's links are this +design's choices under ADR 0656, so that one call traces research to the running build +with no text match (research 085, Findings 7). The issue tracker gains link kinds from an issue to the decision and to the research effort +it carries, made by a tool call and never by text, as ADR 0493 §4 has every link made; the knowledge base gains a +typed link from a decision to the milestone of the issue tracker that builds it, made by a tool call, which joins the +link kinds of its link-fits rule (design 55), beside the `became` link a research effort gets at its graduation +(playbook 02). How it is checked: whole (gate), one ask per milestone, withdrawn by a new head; +a waiter on one member's pull request wakes only on that member's transition. Whole (issue tracker, knowledge base): +an issue linked to a decision and to a research effort reads both back; a decision linked to its milestone reads it +back. Live: research 085's milestone's record reads back research 085 to its decisions, its milestone, its issues and +its pull requests with no text match. + +§319 **A session hears that a record was accepted** (issue 595). The knowledge base already says, on its holder's own +stream, the event `reviewed` for every judgement of a record; from milestone G that event carries, when the operator's +Approve accepts a record, that it was accepted and by the operator, with the warrant, and the tool runner's account is +granted to hear it, so a session waiting on a record wakes on it. The design's choice under ADR 0596 §1 (a module's +events are subscribed to): a new event of the seat `knowledge-base` would be a second path for the same fact (lead-47, +2026-11-09). How it is checked: in the knowledge base's whole-module test an Approve that accepts a record says +`reviewed` with what was accepted and by whom, and the tool runner's account hears it. + +## What retires at milestone G + +- §320 At research 085's milestone's delivery: the indexer's map from language to build module, and the checker's own + module names and its cases for a change to the manifest validator and to the Go build module (ADR 0656); a build + module's edge as a trigger, with the extra build per recipe change (ADR 0603 §3, as ADR 0656 changes it); composing + from manifests a module whose changed build module the check cannot run (ADR 0603 §10 and ADR 0614 §7, as ADR 0656 + changes them); the module graph's one key per module with five past values as what parts read (ADR 0603 §2, as + ADR 0656 changes it); the tiers worked out from five live sources (ADR 0603 §6, as ADR 0656 changes it); a seat + list compiled into a part, and every read of another module's bus state the revision view replaces (ADR 0656); the + gate's `merges`, carried into the delivery records and written no more, and its `owed`, said as events; the hold, + the merge turns, their stalled line and tools and the setting `check-gates-merge`, once their record is accepted; + the last three reads of a pull request's description (ADR 0659 §4); every delivery part's call of the forge module's + own tools, for the seat `git`'s verbs; and the four seat-list readers the delivery parts wire by hand. +- §321 At milestone G's delivery, from research 086 (ADR 0666, ADR 0667): the tagged versions, `tag-wanted` and + `tag-asks` (§373); the checker (`mesh-delivery-checker`), its seat, + its try with `tryevent` and the indexer's try candidates, its kept verdicts and carries (the runner's from then), the + checking program and `mesh/check`, and its buckets and bus account, removed with the gate's forge merge token only + after the first pipeline after G's delivery has delivered a change end to end and on the operator's one word, its + buckets through `drop` after a snapshot whose restore is tested (ADR 0667 §12); the bundle of steps run before a merge; the Approve per + merge, the milestone ask and its three events, and the gate's merges; the gate's merge states, one key per pull request and its verbs `table`, `recheck` and + `close`; the trunk-only want; `source.main`, for one source revision per watched branch; every fixed branch, + repository and module name in code logic; the recovery merge verb this design sketched, never built; and the holders' + trusted `callers` values, for the seat definitions' `callers`. The words **merge check**, **try**, **trunk commit** + and **merge turn** go to *Not:* lines with the as-is update after that delivery, as the glossary's entries say. diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md similarity index 56% rename from 03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md rename to 03-DESIGN/01-to-be/47-milestones/G-7-answers.md index 5a500219..23705ae9 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-retires-and-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -8,208 +8,26 @@ decisions: - "ADR 0668" - "ADR 0667" - "ADR 0666" - - "ADR 0660" - - "ADR 0659" - "ADR 0656" - "ADR 0635" - "ADR 0625" - "ADR 0619" - - "ADR 0614" - "ADR 0603" - "ADR 0596" - "ADR 0595" + - "ADR 0584" + - "ADR 0573" - "ADR 0564" - "ADR 0563" - - "ADR 0551" - "ADR 0548" - - "ADR 0534" - - "ADR 0493" - - "ADR 0443" - - "ADR 0441" - - "ADR 0398" - "ADR 0363" --- -# 47, milestone G: the verbs G adds, what retires, and the answers to the builders' and the reviewers' questions +# 47, milestone G: the answers to the builders', the reviewers' and the operator's questions 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". -## The verbs milestone G adds - -§304 From research 085's milestone the seat gains: - -| Verb or event | Answers or does | -|---|---| -| `groups` with a group | that group's members, which the forge bridge reads in place of the gate's `merges` state (ADR 0656), and the acts the gate said it owes the forge for it, each with its id and its recorded outcome (*What the gate owes the forge*) | -| `owed` | an act the gate owes the forge (a merge, a status, the delivery's view, a comment, a line on the commit's note), with its pull request, head and id, on `mesh.seat.mesh-delivery.event.owed..`, said only from the gate's account and heard by the forge bridge through a durable consumer (*What the gate owes the forge*) | -| `milestone-asked`, `milestone-withdrawn`, `milestone-answered` | until G's delivery: the one Approve ask per milestone (ADR 0493 §9 as ADR 0659 §1 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.`, the milestone in the 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`. G's delivery ends the ask and its three events with the Approve per merge (ADR 0666 §10) | -| `record-opened` | a delivery record opened, which the watchman times for `record-open` | -| `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`, `room` | a delivery record ended; the ledger past 80 % of its cap; the ledger full; the ledger back below its cap (*The delivery record*) | - -§305 From milestone G's delivery the seat's `table`, `recheck` and `close` go with the merge states (*The state table*), -the milestone ask and its three events with the Approve per merge, and it gains (ADR 0666 §6, §26): - -| Verb or event | Answers or does | -|---|---| -| `source` with a watched branch | that branch's source revision; the indexer and the node-engine's catch-up read it, never the gate's state | -| `source-moved` | a new source revision of a watched branch | -| `recovery` | marks a merge its author names a recovery (the commit and why), its record marked `recovery` with the caller the runtime proves | -| `record-opened`, `recovery-merged` | a delivery record opened; a merge named a recovery, which the teller passes on to every channel | - -§306 **The seat `mesh-pipeline-runner`**, from milestone G's delivery, defined in `mesh-delivery-contracts` with the callers -of each verb (ADR 0666 §7, §20, §25): - -| Verb | Answers or does | Callers | -|---|---|---| -| `run` | the run on request of one or more members, each a module at a named commit; delivers nothing | the sessions (the agents' bus identities the tool runner proves) and the operator, nothing else | -| `start` | a delivering pipeline for a watched branch's head, by hand | the operator, the healers | -| `show`, `list`, `stands`, `why-carried`, `kept` | one pipeline whole with its step log; the pipelines and runs of a module, a branch or a commit; what stands still; where a result was carried from; the kept results | any person, an agent session, the healers | -| `review` | a code review's result for a commit and fingerprint | the reviewers the definition names; refused from the commit's author | -| `rerun`, `accept`, `cancel`, `ask-again` | one step again; a failed `optional` step accepted with why; a pipeline ended, its asks dropped; a declined `approval` asked again | the operator, the healers (`accept` and `ask-again` the operator) | - -§307 **The seat `mesh-catalog`** gains, from milestone G's delivery, `register`, `policy`, `watch`, `unwatch` and -`registrations`, and the event `registration-changed` (*A module's registration*), and `catalog-revision` names its -watched branch in its subject. **The build seats** gain the verb -`steps` and the accepted kind `step`, and **the seat `mesh-wants`** the kind `step`, each saying `step-ended`. **The -seat `mesh-builds`**, new and held once for the mesh, says `package-published` (*A publish rebuilds*). - -§308 **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 -each step says*. - -§309 **The seat `node-engine`**'s event `running..` carries, from research 085's milestone, the module, its -fingerprint and the digest of its build's manifest, so the node-engine of another node and the grants part decide a -seat's pass from it (ADR 0660 §2; *Who holds a seat is applied by one rule*). No verb is added. - -§310 **The seat `git`.** The seat `git` (design 26, *The git seat*), of the Delivery domain, defined in -`mesh-delivery-contracts`, holds no verb today, and the delivery parts and the gate reach the forge by naming the forge -module's own tools and events and guessing the shape of their answers (on 2026-11-09 one guess, an answer wrapped in a -`result` field, made 316 conditions stand, issue 1966). From research 085's milestone the seat's definition carries the -verbs and events below, their inputs and answers typed in its contract, served and said by the module that claims the -seat; every delivery part and the gate call and hear the seat, never the forge module by name (acceptance test 27). -They are this design's choice under ADR 0596 §1, §2 (a module's events are subscribed to and its data asked through -its seat verbs) and ADR 0625 §1 (a seat's contract is generated from its definition). The table is built from what the -parts' manifests declare in `invokes` and `consumes` and what their code calls, read on main on 2026-11-09 (mesh-delivery -042b8d1f, mesh-catalog's gate); a declaration nothing calls (the checker's `gitea_commits`, `gitea_compare`, -`gitea_list_branches`) gets no verb and is dropped from its manifest: - -| Verb | Answers or does | Called by (today's call) | -|---|---|---| -| `pull` | one pull request, by its repository and number or by its merge commit: number, base, head branch, head, merge commit, author, state, and `merging` while the forge merges it; an unknown one answers the error kind `not-found` | the forge bridge (a merge the gate did not make; `gitea_api`), the gate (a stalled line's author) | -| `branch` | from milestone G's delivery: a branch's head, and whether a protection rule refuses a direct push and admits only merges | the indexer (`register`, `watch`), the forge bridge (a watched branch's head) | -| `open-pulls` | every open pull request of every repository, its head and base, the statuses its base requires and those its head has | the forge bridge at its start and each reconnect (ADR 0443 §5), the indexer, the gate (`gitea_open_pulls`) | -| `tags` | a repository's tags, paged, the page's bound said when more exist | the forge bridge, for the builder's floor (`gitea_api`) | -| `trees` | the trees of one commit | the forge bridge, the checker, the indexer (`gitea_commit_trees`) | -| `file` | one file at one commit | the forge bridge, the checker, the indexer (`gitea_get_file`) | -| `contains` | whether one commit contains another | the indexer, the gate (`gitea_contains`) | -| `statuses` | the statuses a commit carries | the gate (`gitea_commit_statuses`) | -| `status`, `comment`, `view`, `note` | a commit status, a comment, the delivery's one view comment, a line on the commit's note under `refs/notes/mesh-plan`; a comment and a note line carry the act's id and an id already held is skipped | the forge bridge alone, performing the gate's and the checker's `owed` (`gitea_commit_status`, `gitea_add_comment`, `gitea_delivery_view`, `gitea_note_append`) | -| `merge-pull` | until G's delivery: merges a pull request at the head the operator's Approve names; refuses a head that moved, a branch no rule guards, a head the Approve does not name and a merge git refuses; judges no check (below). From G's delivery: merges with the forge tools' account (ADR 0398 §2) at the head the caller names, judging that it is still the pull request's head and that the branch's protection refuses a direct push, the protection read again on every `branch-moved`, and refusing a head when any run at its build-input fingerprint failed a `required` step, judged from its own state `failed-steps` heard from the runner's `step-ended`, never by a call; a head with no run yet or a passed one merges, it never waits for a run and never asks an Approve (ADR 0666 §11). A pull request already merged at that head is answered performed | until G's delivery the forge bridge alone (`gitea_merge_pull_request`); from it any session the tool runner proves, and the operator | - -| Event of the seat | Says | Heard by (today's event) | -|---|---|---| -| `pull.updated`, `pull.merged`, `pull.closed` | a pull request's new head, its merge with the merge commit, its close | the forge bridge, the gate (`gitea.pull.*`) | -| `forge.pull`, `forge.status`, `forge.delete` | the forge's own webhook of a pull request, of a commit status, of a deleted branch | the gate (`forge.pull`, `forge.status`), the forge bridge (`forge.delete`) (`gitea.forge.*`) | -| `repo.writable` | a repository the forge can write again | the gate (`gitea.repo.writable`) | -| `branch-moved` | from milestone G's delivery: a branch's new head, by a merge or an admitted push, from the forge's push webhook | the forge bridge | - -§311 Each is said on `mesh.seat.git.event.`, declared in the seat's definition, and heard through the hearer's -`consumes` of the seat. - -- §312 **A failed read answers an error, never an empty answer.** When the forge does not answer, refuses or answers what - the contract cannot type, the verb answers an error naming why; an empty list or an absent file is answered only - when the forge said so. A caller never reads an error as "none". How it is checked: the seat's fixtures hold an - error answer for each verb, and each caller's unit table has a row for it (the forge bridge, the checker, the - indexer, the gate). -- §313 **Who may call an act.** The holder's trusted config value `callers`, which today names per verb who may call the - two package seats' verbs (design 51), gains a line per verb of the seat `git`: the act verbs name the forge bridge - alone, the reads name each part that declares them in `invokes`; the grants part grants by it, and the holder - refuses a caller it does not name. From G's delivery the callers per verb are the seat definition's field `callers`, - the grants part's one source (ADR 0666 §20): `status`, `comment`, `view` and `note` the forge bridge alone, - `merge-pull` the persons and agent sessions the tool runner proves, the reads the parts that declare them. The forge module's own acting tools (`gitea_merge_pull_request`, - `gitea_commit_status`, `gitea_delivery_view`, `gitea_note_append`, `gitea_add_comment`) retire with the milestone, - and so do its watch of the checker's state `owed` and its read of the gate's `source`, so the bridge stays the one - part that acts on the forge. How it is checked: test 51 reads the grants, not the code. -- §314 **`merge-pull` judges no check, and from G's delivery no Approve, only the runs at the head's fingerprint.** The operator's answer that no check gates delivery (Telegram, 2026-11-09 01:11 UTC, - "Remove them in G") covers the forge module's own setting `check-gates-merge` as well as the gate's: both are - removed, and `merge-pull` merges an approved head whatever its `mesh/check` says. The record that ends ADR 0551 and - ADR 0548 §2 to §4, §9 and §10 also changes ADR 0534 §4's judgement of `mesh/check` by the merge; it is to be accepted - before the push (*Nothing merges into a repository under check*). From G's delivery it judges no Approve either, its caller - is any session the tool runner proves, and it refuses a head when any run at its fingerprint failed a `required` step, - never waiting for one (ADR 0666 §11; *Approval is a step, and a session merges a pull request*). The reads the - gate would have needed to merge (the forge's word for a conflict, the base's required statuses, the merging account's - write access) are not added: no part of the mesh but `merge-pull` merges, and it answers git's own refusal of a merge - as refused. -- §315 **The order is the module graph's, and each step has its way back.** The verbs are a served-verb edge (ADR 0614): - the forge module's new build runs before the forge bridge's, and the forge bridge's before the gate's, since the - bridge calls the verbs and performs the gate's `owed`, and the gate says `owed` only to a bridge that hears it; all - three are ahead of the acting parts' steps, which read through the seat. While the new forge runs and the old bridge - still calls the retired acting tools, the old bridge's acts fail loudly and stay unperformed in the old gate's keys, - and the switch carries them (*What the gate owes the forge*). A revert goes back in the other order: the gate, then - the bridge, then the forge, each wanted only once the one after it in the forward order runs its reverted build, so - no part calls a verb nobody serves. ADR 0656 §3 stands beside it: the acting parts' new versions act first in their - own steps. Two things cover the gap while the forge is still on its old build and serves no verb of the seat: the - indexer starts on its kept definitions (below), and a check whose read through the seat `git` fails says `could not - run`, naming the forge, and is asked again on the forge module's `running` (*A check runs to its end*). -- §316 **A part that cannot read the forge keeps what it has, and says so.** The indexer, at its start, waits for the - seat `git` at most 2 minutes; with no answer it judges from the last definitions it kept, says in its - `_first_views` that it waits for `git` and since when, writes `view-waits` after five minutes, and reads the - definitions once the seat answers; it then judges again every manifest it judged from kept definitions. Two minutes is longer than a forge's restart, and short enough that a start is - not held behind a forge down for good. How it is checked: test 54. -- §317 **How it is checked.** The seat definition's conformance fixtures hold one answer and one error of each verb and one - of each event, and the forge module's tool tests answer each fixture; the forge bridge's, the checker's, the - indexer's and the gate's unit fakes answer from the same fixtures; a call of an act verb from an account other than - the forge bridge's is refused (test 51); live, a search of the delivery parts' and the gate's code finds no name of - the forge module's tools or events. - -§318 **Links of the trace.** No part reads a review line (ADR 0659 §1 to §3); who may review and the review verb's grant -stay issue 1782's, outside milestone G, and that verb gates nothing (ADR 0659 §2). The trace's links are this -design's choices under ADR 0656, so that one call traces research to the running build -with no text match (research 085, Findings 7). The issue tracker gains link kinds from an issue to the decision and to the research effort -it carries, made by a tool call and never by text, as ADR 0493 §4 has every link made; the knowledge base gains a -typed link from a decision to the milestone of the issue tracker that builds it, made by a tool call, which joins the -link kinds of its link-fits rule (design 55), beside the `became` link a research effort gets at its graduation -(playbook 02). How it is checked: whole (gate), one ask per milestone, withdrawn by a new head; -a waiter on one member's pull request wakes only on that member's transition. Whole (issue tracker, knowledge base): -an issue linked to a decision and to a research effort reads both back; a decision linked to its milestone reads it -back. Live: research 085's milestone's record reads back research 085 to its decisions, its milestone, its issues and -its pull requests with no text match. - -§319 **A session hears that a record was accepted** (issue 595). The knowledge base already says, on its holder's own -stream, the event `reviewed` for every judgement of a record; from milestone G that event carries, when the operator's -Approve accepts a record, that it was accepted and by the operator, with the warrant, and the tool runner's account is -granted to hear it, so a session waiting on a record wakes on it. The design's choice under ADR 0596 §1 (a module's -events are subscribed to): a new event of the seat `knowledge-base` would be a second path for the same fact (lead-47, -2026-11-09). How it is checked: in the knowledge base's whole-module test an Approve that accepts a record says -`reviewed` with what was accepted and by whom, and the tool runner's account hears it. - -## What retires at milestone G - -- §320 At research 085's milestone's delivery: the indexer's map from language to build module, and the checker's own - module names and its cases for a change to the manifest validator and to the Go build module (ADR 0656); a build - module's edge as a trigger, with the extra build per recipe change (ADR 0603 §3, as ADR 0656 changes it); composing - from manifests a module whose changed build module the check cannot run (ADR 0603 §10 and ADR 0614 §7, as ADR 0656 - changes them); the module graph's one key per module with five past values as what parts read (ADR 0603 §2, as - ADR 0656 changes it); the tiers worked out from five live sources (ADR 0603 §6, as ADR 0656 changes it); a seat - list compiled into a part, and every read of another module's bus state the revision view replaces (ADR 0656); the - gate's `merges`, carried into the delivery records and written no more, and its `owed`, said as events; the hold, - the merge turns, their stalled line and tools and the setting `check-gates-merge`, once their record is accepted; - the last three reads of a pull request's description (ADR 0659 §4); every delivery part's call of the forge module's - own tools, for the seat `git`'s verbs; and the four seat-list readers the delivery parts wire by hand. -- §321 At milestone G's delivery, from research 086 (ADR 0666, ADR 0667): the tagged versions, `tag-wanted` and - `tag-asks` (§373); the checker (`mesh-delivery-checker`), its seat, - its try with `tryevent` and the indexer's try candidates, its kept verdicts and carries (the runner's from then), the - checking program and `mesh/check`, and its buckets and bus account, removed with the gate's forge merge token only - after the first pipeline after G's delivery has delivered a change end to end and on the operator's one word, its - buckets through `drop` after a snapshot whose restore is tested (ADR 0667 §12); the bundle of steps run before a merge; the Approve per - merge, the milestone ask and its three events, and the gate's merges; the gate's merge states, one key per pull request and its verbs `table`, `recheck` and - `close`; the trunk-only want; `source.main`, for one source revision per watched branch; every fixed branch, - repository and module name in code logic; the recovery merge verb this design sketched, never built; and the holders' - trusted `callers` values, for the seat definitions' `callers`. The words **merge check**, **try**, **trunk commit** - and **merge turn** go to *Not:* lines with the as-is update after that delivery, as the glossary's entries say. - ## What milestone G's builders found open, answered by this design (2026-11-10) *ADR 0666, ADR 0667, ADR 0668; ADR 0656, ADR 0596, ADR 0595 §4, §9, ADR 0619 §1, §2, ADR 0563, ADR 0564 §2, ADR 0625 @@ -406,7 +224,7 @@ record it follows from, and replaces the builder's own choice where one was made - §351 **One rule for asking again.** mesh-sdk's kept-call form (ADR 0668 §7) asks an ask again only while it is open and not replaced, and every asker and every hand ask-again tool uses it. How it is checked: unit (mesh-sdk), a replaced ask is not asked again. -- §352 **The assigner's newer-revision check reads its own state.** The want gains no field (*Composing waits per +- §352 **The assigner's newer-revision check reads its own state.** The check adds no field to the want (*Composing waits per node*): the assigner keeps in its old `state` section `written` one key per module and watched branch, the source revision and end count it last wrote that module's wants from, compares a new `pipeline-ended` with it by compare-and-set, and deletes the key when `registration-changed` says the branch is no longer watched; its tool @@ -523,63 +341,80 @@ record it follows from, and replaces the builder's own choice where one was made and `builder_untag` go at G's delivery (design 51, *Tagged versions*). Why: the operator, 2026-11-10 about 21:40 CET, no custom versions for now, a tagged version being one; a run delivers nothing (ADR 0666 §18), so it needs no version. How it is checked: test 103. -- §374 **A release records, per edge, the part it was built against or delivered after.** The catalog records on - each released version (§349), in its key of `versions`, one entry per catalog-graph edge of the module's artifacts - (the six kinds of *Dependencies between artifacts*), a lockfile per released version. An entry names the kind, the - dependency's artifact reference and what the edge depended on: - - kinds 1 and 3 (built against): the dependency's version and the digest of that artifact, as the build used them. - The build module names each dependency it resolved, version and digest, in its step key, and the builder carries - them in each artifact's `package-published`, so the indexer reads no other part's state (ADR 0596). A build is on - one build node, so these entries are per release. - - kinds 2 and 4 (delivered after): per node, the dependency's version and artifact digest running on that node - when the released version first runs there, heard from the node-engine seat's `running` events through a durable - consumer of the indexer's own, the fingerprint mapped to its version and digest in `versions`. +- §374 **A release records, per edge, the part it was built against or delivered after.** One version per module + stays (§52), and an edge is judged by the digest of the part it points to. The catalog records on each released + version (§349), in its key of `versions`, one entry per catalog-graph edge of the module's artifacts (the six kinds + of *Dependencies between artifacts*), a lockfile per released version. An entry names the kind, whether the edge + makes the dependent stale or only orders it (§378), the manifest word it came from, the part it points to, and what + the edge depended on: + - kind 1 (an imported package) and kind 3 (the build toolchain): the dependency's version and the digest of the part, + as the build used them. The build module names each dependency it resolved, version and digest, in its step key, + and the builder carries them in each artifact's `package-published`, so the indexer reads no other part's state + (ADR 0596). Kind 3 points at the toolchain part the agreed toolchain names (ADR 0584 §1), never at the build + module's own program. + - kinds 2 and 4 (delivered after): the dependency's version and part digest the catalog last said + `version-delivered` for when the release is delivered (§376), one entry for the mesh. - kind 5 (has acted for): a fact, not a version: the acting part, its step, the build that acted and that the act was done (or released by hand), from the holder's `wait-ended` (*Each step's wait is its own part's*). - - kind 6 (runs a version that serves): a fact, per node: the verb or manifest word used and the dependency's build - on that node that serves it, from the node-engine seat's `running` and the catalog entry of that build. + - kind 6 (runs a version that serves): a fact: the verb or manifest word used and the dependency's delivered + version that serves it. A build of a watched branch other than the registration's first releases no version (§371): its entries are kept in the indexer's old `state` section `build-edges`, one key per module and watched branch holding the newest build's entries, keyed by its fingerprint, replaced by the next build and deleted when `registration-changed` says the - branch is no longer watched. The declared edge stays in the catalog graph; an entry is the release's or the build's - fact, never changed after. Why: the operator, 2026-11-10 about 22:10 CET (issue 1961); a version is per module and an - edge per artifact, so the artifact's digest is what an edge compares (the lead's review of these lines, same - evening). Each entry is about 0.1 KiB, about ten a version and four nodes for the delivery kinds, so `versions` - stays within its 64 MiB (an estimate). How it is checked: test 104. + branch is no longer watched, and they compare by the part's digest alike. The declared edge stays in the catalog + graph; an entry is the release's or the build's fact, never changed after. Why: the operator, 2026-11-10 about 22:30 to 23:00 CET and 22:10 CET (issue 1961), and + the lead's review of these lines. Each entry is about 0.1 KiB, about ten a version, so `versions` stays within its + 64 MiB (an estimate). How it is checked: test 104. - §375 **One query finds every edge behind, by the part's digest.** The seat `mesh-catalog`'s verb `outdated`, with - the indexer's tool `indexer_outdated`, answers every entry that is behind: for kinds 1 and 2, the recorded artifact - digest differs from that artifact's digest in the dependency's newest released version (for a build of another - branch, its newest build on that branch); for kinds 3 and 4, it differs from the digest the dependency's newest build - has where it acts (for kind 3 the agreed toolchain, for kind 4 what runs on that node); for kind 5, the newest version - of the acting part has not acted for the dependent's current build; for kind 6, the build on that node no longer - serves the verb or word. A dependency's newer version that left the artifact's digest as it was makes nothing behind, - so nothing is done twice for the same inputs. Each answer names the module and its version or build, the node for a - per-node entry, the kind, the dependency, what was recorded and what is newest, and the act that catches it up. Its - callers are any person, an agent session, the healers, the runner and the assigner; `indexer_outdated_again`, a hand - act written in the hand-act log, says the event that starts an entry's work again. At its start, the runner and the - assigner each ask `outdated` once and start the work it names, so a restart or a missed event leaves nothing behind. - Why: the operator, 2026-11-10 (issue 1961's "outdated"); events start the work, and the query finds what is behind - and answers a person. How it is checked: test 105. -- §376 **Each kind is checked on its own event, per node where it is delivered.** After a dependency's version is - released, on its `version-released`: the dependents by kind 1 whose entry is behind are rebuilt against it and their - pipelines started, as on `package-published` (§53), and those by kind 2 are delivered again on each node whose entry - is behind, the assigner writing that node's want again. After a dependency is delivered: by kind 3, when the agreed - toolchain names the build module's new build (*A build module's wait ends on the agreed toolchain*), its dependents - whose entry is behind are rebuilt; by kinds 4 and 6, on the node-engine seat's `running..` of the - dependency's new build on a node, the dependents on that node whose entry is behind are delivered again by their - wants there, so a dependent on one node follows its own node and waits for no other. The catalog also says - `version-delivered` (the module and the version, once every present node that places it runs it healthy, nodes away - named, ADR 0603 §7) on `mesh.seat.mesh-catalog.event.version-delivered`, kept 90 days at 16 MiB: the mesh-wide summary - a person and the status view read, which starts no per-node work. Kind 5 is held by the acting parts' waits - (*Within a revision the new version acts first*) and only shown by `outdated`. Why: the operator, 2026-11-10; the - events still start the work (ADR 0666 §14, §15); per node, since a node away holds only itself (ADR 0603 §7). How it - is checked: tests 104 and 105. + the indexer's tool `indexer_outdated`, answers every entry that is behind: for kind 1, the recorded digest differs + from the part's digest in the dependency's newest released version (for a build of another branch, its newest + build on that branch); for kind 3, it differs from the toolchain part the agreed toolchain names now; for kinds 2 and + 4, it differs from the dependency's newest delivered version; for kind 5, the newest version of the acting part has + not acted for the dependent's current build; for kind 6, the delivered version no longer serves the verb or word. A + dependency's newer version that left the part's digest as it was makes nothing behind, so nothing is done twice for + the same inputs. Only a stale edge (§378) that is behind has an act that catches it up, a rebuild; an ordering edge + behind is shown and starts nothing. Each answer names the module and its version or build, the kind, the + dependency, what was recorded and what is newest, and the act. Its callers are any person, an agent session, the + healers, the runner and the assigner; `indexer_outdated_again`, a hand act written in the hand-act log, says the + event that starts a stale entry's rebuild again. At its start, the runner asks `outdated` once and starts the + rebuilds it names, so a restart or a missed event leaves nothing behind. Why: the operator, 2026-11-10 (issue + 1961's "outdated"). How it is checked: test 105. +- §376 **A stale edge rebuilds on its event; a delivery is waited for mesh-wide.** On a dependency's `version-released`, + the dependents by kind 1 whose entry is behind are rebuilt against it and their pipelines started, as on + `package-published` (§53); when the agreed toolchain names a new toolchain part, the dependents by kind 3 whose entry + is behind are rebuilt. No other edge rebuilds or delivers anything again. A dependent's new version that a change + orders after its dependency's new version by kind 2, 4 or 6 is wanted only once that dependency's version is + delivered to every machine that places it and is present: the catalog says `version-delivered` (the module, the + version and its parts' digests) once every present machine placing it runs it healthy, from the node-engine seat's + `running`, on `mesh.seat.mesh-catalog.event.version-delivered`, kept 90 days at 16 MiB, and the assigner writes the + dependent's wants on it. A machine away is named and catches up when it is back (ADR 0603 §7); meanwhile the + dependent's status, in `assigner_why_not` and the status view, reads "waiting for delivery of ", naming + the machines it waits for. When every machine that places the dependency is away, it is not delivered: its status + reads "waiting for to be back" and its dependents wait; nothing is raised for a machine simply away. + Kind 5 is held by the acting parts' waits (*Within a revision the new version acts first*). Why: the operator, 2026-11-10 about 22:30 to 23:00 CET. How it is + checked: tests 104 and 106. - §377 **The first fill.** At the indexer's first start under G, after `versions` is filled (§372), each module's - newest version gets its entries once: the build kinds from the dependency version published latest before that - version's build was claimed, read with the times of `builder_versions`, and its digest; the delivery kinds, per - node, from the last `running` of each module on each node, which the node-engine seat's stream keeps last per - subject with no age limit; kind 5 as done for every acting part's running build; kind 6 from what each node runs. - Each filled entry is marked `filled`, so a person sees it was derived, not recorded. It is not filled again. Why: so - the first `outdated` does not find the whole mesh behind (the lead, 2026-11-10). How it is checked: whole + newest version gets its entries once: kinds 1 and 3 from the dependency version published latest before that + version's build was claimed, read with the times of `builder_versions`, and its part's digest; kinds 2, 4 and 6 from + the versions every present machine runs, read from the last `running` of each module on each node, which the + node-engine seat's stream keeps last per subject with no age limit; kind 5 as done for every acting part's running + build. Each filled entry is marked `filled`, so a person sees it was derived, not recorded. It is not filled again. + Why: so the first `outdated` does not find the whole mesh behind (the lead, 2026-11-10). How it is checked: whole (indexer), after the first fill `outdated` answers nothing for a mesh whose modules run their newest builds. +- §378 **Each edge kind makes its dependent stale or only orders it.** An edge makes the dependent stale when the + dependent is made from the dependency: kind 1, the dependency goes into it (an imported package), and kind 3, the + dependency builds it (its build toolchain part). Every other kind (2, 4, 5 and 6) only orders. A change to a build + module's own code, which is no toolchain part, rebuilds nothing. Why: the operator, 2026-11-10 about 22:30 to 23:00 CET. How it is checked: unit (indexer's + graph), each kind's stale-or-order, and a build module's code change rebuilds no module. +- §379 **A want names the released version and each part's digest.** From G's delivery a want `want..` + names, beside its fingerprint and source revision, the module's released version, for people (none for a build of + another watched branch), and the digest of each of its parts; the node-engine fetches and checks each part by its + digest, never by the version. The node-engine of G reads the two fields, its reader edge of kind 4 delivered before + any want writes them (ADR 0667 §4, §9). Why: the operator, 2026-11-10 about 22:30 to 23:00 CET. How it is checked: whole (assigner, node-engine), a want names + the version and the parts' digests, and a fetched part whose digest differs is not applied. +- §380 **`indexer_edges` answers both directions.** For any module or part, per watched branch, `indexer_edges` + answers what it depends on and what depends on it; per edge its kind, stale or order (§378), the manifest word it + came from, the part it points to, and what its newest release recorded (version and digest, or the fact for kinds 5 + and 6), and whether it is behind now (§375). Why: the operator, 2026-11-10 about 22:30 to 23:00 CET. How it is checked: whole (indexer), a fixture module's + edges are answered from both ends with their newest release's record. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index 47810cf7..dc48292b 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -73,11 +73,12 @@ file on 2026-11-10, and is cited as "design 47 G §n". A sentence that holds a d stayed in the main file, and G builds what it says from G's delivery. A section named in italics is in this part, or, when it is not here, in the main file. -The part is six files, each read whole, its design lines numbered across them: +The part is seven files, each read whole, its design lines numbered across them: - [G-1-pipelines-registration-and-steps](G-1-pipelines-registration-and-steps.md): pipelines per module: registration, watched branches, steps and policy, what holds a delivery, approval, groups, dependencies, a publish. - [G-2-pipelines-sizes-checks-and-tools](G-2-pipelines-sizes-checks-and-tools.md): pipelines per module: any size, the pipeline makes the check, who may say what, health, recovery, tools, kept data, how G itself is delivered, the choices under ADR 0666 and ADR 0667. - [G-3-delivery-record-and-graphs](G-3-delivery-record-and-graphs.md): the delivery record, what the gate owes the forge, the state table, the two graphs and a change's graph difference. - [G-4-revision-view-and-steps](G-4-revision-view-and-steps.md): the revision view, what ADR 0656 leaves to this design, the choices under ADR 0656, what each step says and their tools. - [G-5-acting-parts-check-and-kept-data](G-5-acting-parts-check-and-kept-data.md): the acting parts, the removed hold, the check on the change's own acting parts, after the merge, taken in, and kept data on the bus. -- [G-6-verbs-retires-and-answers](G-6-verbs-retires-and-answers.md): the verbs G adds, what retires, and the answers to the builders' and the reviewers' questions. +- [G-6-verbs-and-retires](G-6-verbs-and-retires.md): the verbs G adds and what retires. +- [G-7-answers](G-7-answers.md): the answers to the builders', the reviewers' and the operator's questions (§322 on). -- 2.54.0 From 27c9c6f11f57b36d1177f65cf51b563f8cf1950d Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 22:30:03 +0100 Subject: [PATCH 09/13] Answer round 2 of the review of milestone G's part: one rebuild trigger, digests from events, every new store sized The four reviewers found two triggers for one rebuild, digests read from the builder's own keys, a mesh-wide wait with no owner, bound or tool, new states with no retention or cap, a first fill the builder could race, and a registry check whose source goes; each is now a design line with its reason, and test 60 follows section 271. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../47-delivery-from-commit-to-delivered.md | 40 ++-- .../G-1-pipelines-registration-and-steps.md | 13 +- .../G-2-pipelines-sizes-checks-and-tools.md | 12 +- .../G-5-acting-parts-check-and-kept-data.md | 9 +- .../47-milestones/G-6-verbs-and-retires.md | 5 +- .../01-to-be/47-milestones/G-7-answers.md | 207 ++++++++++++------ .../51-the-registries-keep-what-is-named.md | 4 +- 7 files changed, 191 insertions(+), 99 deletions(-) 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 dbe070ab..3c170f47 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 @@ -1459,7 +1459,9 @@ the operator for the bus node's want (ADR 0539 §15). naming its fingerprint, which its node composes again (ADR 0547 §2). - **A build before its want.** Every module, the builder's own and the build modules among them, is wanted only from the builder's done key whose artifacts all name a reference; a module whose key is not yet done - waits under the assigner's first rule and fails nothing (ADR 0524 §7, ADR 0539 §18). + waits under the assigner's first rule and fails nothing (ADR 0524 §7, ADR 0539 §18). From G's delivery the assigner + takes a build's digests from `version-released` and the builder's `build.ended`, never its done key (design 47 G + §379). - **History: the switch to release 6 was one plan, release 5's last** (it is delivered; ADR 0547 takes the composing, signing and issuing it gave the controller away again). Release 5's controller ran the group's plan with the new node-engine and the new controller in one tier (issue 1942), so every node's last declaration named the new node-engine and carried the operator's public key, and the control-node's named the new @@ -1754,13 +1756,15 @@ What milestone G's prepare found, and its test: 59. **A session hears an accepted record** (issue 595). An Approve that accepts a record makes the knowledge base say `reviewed` with what was accepted, by the operator and with the warrant, and the tool runner's account hears it, so a session waiting with `mesh_wait` wakes on it (whole, knowledge base; live). -60. **The store's holder takes its own build when its seat is broken.** With a fixture holder of `oci-artifact-registry` - that refuses every store, its new build is kept on the build node, named in the builder's done key, fetched by the - node-engine through `kept-artifact`, and once it runs the next build is stored through the seat, with no hand act; - a call of `kept-artifact` from an account other than a node-engine's is refused; it is refused for a module that - does not claim `oci-artifact-registry`; fetched bytes that differ from the done key's digest are not applied; an - artifact over the bus's value limit is fetched whole as an object; with the keeping node away, or the build swept, - the want waits and the have says why (whole, builder, build module and node-engine). +60. **The store's holder takes its own build when its seat is broken** (design 47 G §271). With a fixture holder of + `oci-artifact-registry` that refuses every store, its new build is kept on the build node, the builder's + `build.ended` naming it kept there with its digest, and the want naming that digest; the node-engine fetches it + through `kept-artifact` and checks it against the want's digest, and once it runs the next build is stored through + the seat, with no hand act; a call of `kept-artifact` from an account other than a node-engine's is refused; it is + refused for a module that claims the seat but is not its holder in the seat list; fetched bytes that differ from + the want's digest are not applied; an artifact over the bus's value limit is fetched whole as an object; with the + keeping node away, or the build swept, the want waits, the have says why, `kept-build-waits` is raised after 60 + minutes and cleared once the have runs the want (whole, builder, build module and node-engine). 61. **No module reads the indexer's `modules`.** The watchman's manifest findings, the status view's `view_reach`, the 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 @@ -1927,27 +1931,33 @@ ADR 0667's checks, of milestone G itself: reporting it; with one of its two artifacts published `1.4.3` is not released, and `builder_publish_again` publishes the other under `1.4.3`, which the catalog then says `version-released` once; a new `1.5` builds as `1.5.0`; two builds close together reserve two versions; a `beta` build reserves none and is delivered; a manifest - saying `1.4-beta` is refused (whole, builder, indexer and Go build module, mesh-delivery and mesh-catalog). + saying `1.4-beta` is refused; `builder_publish_again` names `1.4.3`; a `1.4.2` artifact published after `1.4.3` is + released late, said by no `version-released` and rebuilds nothing; a publish of an unreserved version is refused + by the registry holder (whole, builder, indexer and Go build module, mesh-delivery and mesh-catalog). 103. **A group's run on request publishes no version** (design 47 G §373). A run on request of a group changing a - package and a module importing it builds the module against the package member's build by digest, publishes and - reserves nothing, and no `tag-wanted` is said (whole, runner, builder and Go build module, mesh-delivery and + package and a module importing it builds the module against the package member's build by the digest the builder + took from its own `build.ended`, the fetched bytes checked against it; a member whose build is not done holds the + dependent with a `wait-held` naming it, and one whose build failed fails the dependent's step naming it; the run + publishes and reserves nothing, and no `tag-wanted` is said (whole, runner, builder and Go build module, mesh-delivery and mesh-catalog). 104. **A release records its dependencies, and only a stale edge rebuilds** (design 47 G §374, §376, §378). A module released at `1.4.3`, built against mesh-sdk `0.1.15` and delivered after a provider at `2.0.1`, holds - those versions with their parts' digests; mesh-sdk's `version-released` of `0.1.16` that changes the imported - package rebuilds it, one that leaves its digest as it was rebuilds nothing, and a change of the Go build module's + those versions with their parts' digests; mesh-sdk's `package-published` of `0.1.16` that changes the imported + package rebuilds it, started by the runner after its `outdated` ask, one that leaves its digest as it was rebuilds nothing, and a change of the Go build module's own code rebuilds no module; a `beta` build's entries compare by digest (whole, indexer, builder and runner, mesh-delivery). 105. **One query finds what is behind** (design 47 G §375). With the runner stopped while mesh-sdk `0.1.16` is released, `outdated` names the module's kind-1 edge at `0.1.15` against `0.1.16`, and at its restart the runner - starts that module's pipeline once; `indexer_outdated_again` says the event again and writes the hand-act log + starts that module's pipeline once, keyed by mesh-sdk's source revision; `runner_catch_up` does it by hand and + writes the hand-act log (whole, indexer and runner, mesh-delivery). After the first fill (§377), a mesh running its newest builds is answered with nothing behind. 106. **A delivery is waited for mesh-wide** (design 47 G §376, §379, §380). A module ordered after a provider's new version is wanted only once `version-delivered` says every present machine placing the provider runs it; with one machine away the module's status reads "waiting for delivery of" the provider, naming the machine, and it catches up when that machine is back; with every placing machine away the provider reads "waiting for - to be back" and nothing is raised; each want names the released version and its parts' digests; `indexer_edges` + to be back" and nothing is raised; with a placing machine present that never runs it, `delivery-waits` is raised + after 60 minutes naming it and its node-engine's version; each want names the released version and its parts' digests; `indexer_edges` answers the module's edges from both ends (whole, indexer, assigner and node-engine, mesh-delivery and `mesh-host`). ## What retires diff --git a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md index 9c3ebcbe..0e470faa 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md +++ b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md @@ -391,9 +391,16 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l starts, where ADR 0666 §15 numbers a package at its publication and design 51 publishes a held digest under a new number: a packed file carries its version, so it must be known before the build; the operator's choice, which a later research brings the records in line with. How it is checked: test 102. -- §53 **Every dependent is rebuilt against it.** On `package-published` every artifact with a kind-1 edge to the package is - rebuilt against the new version, and the runner, which consumes the event, starts its pipeline; nothing else is - rebuilt, and a change to tests alone publishes nothing (ADR 0666 §15; research 086, R18). +- §53 **Every dependent behind is rebuilt against it, on `package-published`.** The builder says `package-published` + for every package build of a watched branch: with its released version on the registration's first watched branch, + and on another watched branch by its digest alone, the build stored by digest with no version (§371). The runner, + which consumes it, asks the catalog's verb `outdated` for that package on that branch, a kept call (ADR 0668 §1, + §2), and starts a pipeline for each dependent by kind 1 whose recorded part digest differs from the published one: + the catalog compares (§375), the runner starts, and no other part rebuilds anything. A `package-published` whose + version is lower than the highest the catalog holds released for that module starts nothing (§382). Nothing else is + rebuilt, and a change to tests alone publishes nothing (ADR 0666 §15; research 086, R18). Why: ADR 0666 §15 has the + runner consume `package-published`; the digest comparison is the operator's (2026-11-10), and one event for both + branches is the design's choice, round 2 of the review by architectural domain, 2026-11-10. - §54 **A second start writes nothing.** The runner starts a pipeline on two events, the indexer's `take-in.taken` (ADR 0666 §13) and `package-published` (ADR 0666 §15). When both reach it for the same source revision and module, the second start writes nothing: the runner writes a pipeline's key (watched branch, source revision and module, *The diff --git a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md index 82f9cf58..8d4b576d 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md +++ b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md @@ -162,7 +162,8 @@ One file of milestone G's part of [design 47](../47-delivery-from-commit-to-deli its members, and a merge behind a pending revision, are governed by their own bounds (ADR 0666 §24). At its start the watchman seeds its open waits from the runner's `stands`, as it seeds `take-in-waits` from `indexer_pending`. - §76 **The runner's own health.** Its manifest declares its health check, run outside its process (ADR 0240): the runner - answers `show`, and its durable consumers of `step-ended`, `take-in.taken`, `package-published` and `decided` are + answers `show`, and its durable consumers of `step-ended`, `take-in.taken`, `package-published`, `toolchain-agreed` + and `decided` are each less than five minutes behind; a failed check names which. - §77 **The runner waits for its view.** With no revision view it says so in `_first_views` and raises `view-waits` after five minutes, as every part does (ADR 0656 §6). @@ -223,6 +224,15 @@ and an estimated 2 mesh-sdk publishes a day, each rebuilding about 120 artifacts | the runner | `step-log`: one entry per step's start and end, its output, its pipeline and the `kept` digests it wrote or carried, by name | old `streams` section | by its age, 100 days, ten days past the longest retention it ages (§330) | 128 MiB; about 3,600 entries a day of about 0.2 KiB is about 70 MiB at 100 days | | the runner | `step-outputs`: a lint or test step's output | old `objects` section | 7 days | 1 GiB; about 1,200 steps run a day (the rest carried) of about 50 KiB is about 420 MiB at 7 days | | the runner | `kept`: a package's passing result per level, by digest, with `last-read`, written by each carry that reads it | old `state` section | 30 days after `last-read` | 128 MiB; a publish changes the digest of about 1,000 importing packages at two levels, so about 4,000 entries a day of about 0.3 KiB, about 36 MiB at 30 days | +| the indexer | `versions`: one key per module and version, its artifacts' digests, the source revision and its edge entries (§349, §374) | old `state` section | a module's newest 100 versions, never its highest of a `MAJOR.MINOR`; the indexer deletes the older at each release | 64 MiB; about 120 modules of 100 versions at about 3 KiB is about 35 MiB; at the cap it records no release and says `full`, as *A store filling is said before it is full* | +| the indexer | `build-edges`: the newest build's edge entries per module and watched branch other than the first | old `state` section | replaced by the next build; deleted when the branch is unwatched | under 1 MiB | +| the builder | `reserved`: one key per module and `MAJOR.MINOR` (§369) | old `state` section | until its `version-released`, a release of another `MAJOR.MINOR` of the module, or `builder_reserved_drop` | a few KiB; at the cap the builder reserves nothing and says why | +| the assigner | `written`: the last source revision and end count per module and watched branch (§352) | old `state` section | until the branch is unwatched | a few KiB | +| the indexer and the assigner | `inputs`: a `run` ask's input by write time and sha256 (§354) | old `state` section | until the ask's `step-ended`, at most a day | 64 MiB; an estimated 300 asks a day of under 100 KiB each is under 30 MiB; at the cap the asker refuses the ask loudly, naming the store | +| the runner and the assigner | `findings` (§355) | old `state` section | an entry deleted when its finding clears | a few KiB | +| the runner | `aged`: three step-log sequences (§330) | old `state` section | never deleted | bytes | +| the teller | `widens-said` (§335) | the template `state` | until the branch is unwatched | a few KiB; the template's | +| each build module | `steps` (§326) | the template `state` | one key per artifact, dropped by `steps` with `drop` | under 1 MiB; the template's | | the indexer | `registrations` and its stream `registration-history` | old `state` and `streams` sections | the life of the mesh, never derived again | 64 MiB (*A module's registration*) | | the indexer | catalog graph revisions, per watched branch | objects in the old form (ADR 0619 §2) | while a record pins it or it is its branch's current | 512 MiB, as *Two graphs, one source of truth*; a second watched branch on a few modules adds a few MiB | | the gate | `source`: one key per watched branch | its old `state` section, as today | 64 revisions of each key; a branch's key deleted when `registration-changed` says the last registration watching it unwatched it (`unwatch`) | a few KiB | diff --git a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md index 2c767215..8e84e5c9 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md +++ b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md @@ -126,7 +126,8 @@ names go, since every new acting part needed code changes elsewhere (research 08 - While the keeping node is away, or its sweep removed the build, the want waits, and the node-engine says in its have that it waits for that node or for a build no longer kept. A want that waits so for 60 minutes, building's default bound, is the watchman's finding `kept-build-waits`, naming the module, the node and the build node; the - operator or the healers recover it by `builder_build_again`, which builds and keeps it again. + operator or the healers recover it by `builder_build_again`, which builds and keeps it again; the finding clears when + the node's have runs the want. - A node-engine of before G does not know `kept-artifact`: the holder's want on its node waits for its new node-engine (ADR 0667 §9). A new node-engine is itself fetched from the store; while the store is broken it waits, so on a node that has neither the store's holder's new build nor G's node-engine, the want waits and @@ -140,11 +141,6 @@ names go, since every new acting part needed code changes elsewhere (research 08 is checked: test 60, and whole (build module, node-engine): a module claiming the seat but not its holder in the seat list is refused; bytes differing from the want's digest are not applied; a want waiting 60 minutes raises `kept-build-waits` and `builder_build_again` ends it. - - This is the one artifact the mesh delivers from a build node instead of the store, and only for the holder of - `oci-artifact-registry`; the design's choice under ADR 0656 §3 (a wait skips what the holder's own new version needs) and ADR - 0526 §2, whose build by a session's call it makes a step of the delivery instead of a hand act. How it is checked: - test 60. - §272 **The check refuses a cycle among the waits within one step** (ADR 0656, which changes ADR 0603 §2's refusal of a cycle), counted beside the edges ADR 0614 §3 and §6 list; a cycle through an acting part's edges across two steps, or through a holder's own inputs, is none. @@ -173,6 +169,7 @@ names go, since every new acting part needed code changes elsewhere (research 08 | granting | the grants part | `grants_waits` | `grants_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*) | + | delivery of a dependency (kinds 2, 4 and 6), from G's delivery | the assigner, holding a dependent's new version until `version-delivered` of its dependency's (§376) | `assigner_waits` | `assigner_release` (want it now), and `assigner_delivered_again` | | pipeline, from G's delivery | the runner, holding the pipelines its new version is to run until it runs healthy | `runner_waits` | `runner_release` (run them with the running runner) | Each release writes the hand-act log of the seat `mesh-hand-acts`, with who released and why. diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md index fc13b0f3..b37d2d16 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md @@ -66,7 +66,10 @@ of each verb (ADR 0666 §7, §20, §25): `registrations`, and the event `registration-changed` (*A module's registration*), and `catalog-revision` names its watched branch in its subject. **The build seats** gain the verb `steps` and the accepted kind `step`, and **the seat `mesh-wants`** the kind `step`, each saying `step-ended`. **The -seat `mesh-builds`**, new and held once for the mesh, says `package-published` (*A publish rebuilds*). +seat `mesh-builds`**, new and held once for the mesh, says `package-published` and `toolchain-agreed` and serves +`reserved` (*A publish rebuilds*, §369, §376). From G's answers (G-7), `mesh-catalog` also gains the verbs `versions` +and `outdated` and the events `version-released` and `versions-filled`, `mesh-wants` the event `version-delivered`, and +the seat `git` the verb `tag` (§349, §372, §375, §376, §370). §308 **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 diff --git a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md index 23705ae9..c3e1df89 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -100,10 +100,14 @@ record it follows from, and replaces the builder's own choice where one was made `pipeline-started` 32 MiB, `pipeline-ended` 192 MiB, `group-ended` 64 MiB, `run-started` 16 MiB, `step-started` and `step-ended` 128 MiB each, `wait-held` and `wait-ended` 32 MiB each; the seat `git`'s `branch-moved` 64 MiB; each build seat's `step-ended` 128 MiB; `mesh-wants`'s `step-ended` 32 MiB; `mesh-builds`' `package-published` and - `mesh-catalog`'s `registration-changed` 16 MiB each; all 90 days. Why: about 300 pipelines a day of about 6 steps + `mesh-catalog`'s `registration-changed` 16 MiB each; `package-published` 128 MiB; `version-released` 128 MiB, + `version-delivered` 64 MiB, `toolchain-agreed` 16 MiB, and `versions-filled` 1 MiB with no age limit; all others + 90 days. At about 300 releases a day (a mesh-sdk publish rebuilding about 120 modules), `package-published` of about + two artifacts each and `version-released` each say about 0.6 MiB a day, about 54 MiB in 90 days, and + `version-delivered` about 27 MiB. Why: about 300 pipelines a day of about 6 steps (*The runner's kept data*) say about 1,800 step events a day of about 0.5 KiB, about 80 MiB in 90 days, and about 300 pipeline ends of about 4 KiB, about 105 MiB, 55 % of its cap; a build seat runs about 1,200 steps a day, about 54 - MiB; publications and registration changes are a few a day; estimates, read from the fill tools once G runs. A kept + MiB; registration changes are a few a day; estimates, read from the fill tools once G runs. A kept seat stream at its cap drops its oldest message, as a stream with a cap does; the grants part, which makes the streams, says `filling` on its seat `mesh-grants` past 80 % and `room` below 70 %, which the watchman's `store-filling` raises and clears as for every store (*A store filling is said before it is full*). How it is @@ -124,7 +128,9 @@ record it follows from, and replaces the builder's own choice where one was made its build was made from. The runner reads the dependents from that revision's catalog graph, the artifacts with a kind-1 edge to the package, and starts each one's pipeline keyed by that branch, that source revision and the dependent module, the version in the pipeline's state; a `take-in.taken` of the same revision and module is the - same pipeline (*A second start writes nothing*). Why: a pipeline's key is branch, source revision and module (above), + same pipeline (*A second start writes nothing*). A pipeline the runner starts from `outdated` (its start, §375, or + `runner_catch_up`) is keyed by the dependency's watched branch, the dependency's source revision `outdated` names, and + the dependent module, so it is the same pipeline the event would have started. Why: a pipeline's key is branch, source revision and module (above), and the publication is made from a revision of a watched branch (ADR 0666 §15). How it is checked: whole (builder, runner), a publish starts one pipeline per dependent, keyed by the publishing revision. - §335 **What the teller says of a widening.** Each entry of `widens` names what widens (`acting-rule`, `grant`, @@ -208,15 +214,19 @@ record it follows from, and replaces the builder's own choice where one was made state. With no holder of a build seat, a build ends `could not run`, naming the seat, and is asked again on the seat list's change. Why: ADR 0596 §2. How it is checked: the builder's account holds no grant to the indexer's state. - §349 **The catalog holds released versions only, and says each.** The indexer keeps, for the seat `mesh-catalog`, - each module's released versions in its old `state` section `versions`, one key per module and `MAJOR.MINOR` holding - its released patches with each artifact's digest and the source revision built (issue 1961's catalog of versions). - It hears every artifact's `package-published` (packages, images, bundles and archives alike) and records a version - as released once every artifact the module's catalog entry names is published at it, then says `version-released` - (the module, the version, its artifacts, the source revision) on `mesh.seat.mesh-catalog.event.version-released`, - kept 90 days at 16 MiB; the verb `versions` answers a module's released versions, its callers the builder and any - person or session. A reserved version is not in it. The builder's `version.numbered` is no longer said or heard. - Why: the operator, 2026-11-10 about 21:40 CET (gaps items 23 and 43). How it is checked: whole (indexer), a version - with one of two artifacts published is not released, and with both is, said once. + each module's released versions in its old `state` section `versions`, one key per module and version holding each + artifact's digest, the source revision built and its edge entries (§374) (issue 1961's catalog of versions). It hears + every artifact's `package-published` (packages, images, bundles and archives alike) and records a version as + released once every artifact the module's catalog entry names is published at it, then says `version-released` (the + module, the version, each artifact's digest, the source revision) on + `mesh.seat.mesh-catalog.event.version-released`. A version lower than the highest it holds released for that module + is recorded, marked `released late`, and not said: no `version-released` is lower than the module's highest, and + nothing follows one (§382). Each module keeps its newest 100 versions: at each release the indexer deletes that + module's oldest beyond them, never its highest of any `MAJOR.MINOR`. The verb `versions` answers a module's released + versions, its callers the builder, the holders of the registry seats and any person or session. A reserved version + is not in it. The builder's `version.numbered` is no longer said or heard. Why: the operator, 2026-11-10 about 21:40 to 23:00 CET (gaps items 23 and 43); the + key per version, the 100 kept and the late release are the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: whole (indexer), a version with + one of two artifacts published is not released, and with both is, said once; a late older version is not said. - §350 **A superseded build's drop is a kept call.** The runner ends a superseded build's step `superseded` at once, and asks the build seat's `drop` as a kept call (ADR 0668 §1, §2), so it is asked again through every transport fault and said `call-unanswered` after five minutes in the runner's findings. How it is checked: whole (runner), a drop @@ -305,42 +315,66 @@ record it follows from, and replaces the builder's own choice where one was made - §368 **A manifest says its `MAJOR.MINOR`.** Each module's manifest names `version`, two numbers `MAJOR.MINOR`; the manifest validator refuses any other form, a pre-release or custom suffix (`beta`, `alpha`) among them: no custom version exists for now. A manifest without it is refused once G's reader runs (ADR 0667 §4: the reader is delivered - first). Why: the operator, 2026-11-10 about 21:40 CET. How it is checked: unit (manifest validator), `1.4` accepted, - `1.4-beta` and `1.4.2` refused. + first). G's change writes `version` into every existing manifest, the `MAJOR.MINOR` of the version §372 fills for that + module, `0.1` for a module with none published, in the same group as the reader. Why: the operator, 2026-11-10 about + 21:40 CET; the values are the design's choice. How it is checked: unit (manifest validator), `1.4` accepted, + `1.4-beta` and `1.4.2` refused; the catalog's validation finds no manifest without `version` at G's heads. - §369 **The reservation is one key per module and `MAJOR.MINOR`.** The builder, a foundation part, keeps in its old - `state` section `reserved` one key per module and `MAJOR.MINOR`, the version its build reserved, written by - compare-and-set at the build's start; a newer build of the module takes the next after the higher of the highest - released and the one reserved, so two builds never share a version, and a superseded build's version is never - released, leaving a gap in the patches. The builder deletes the key when the catalog says `version-released` for it; - `builder_reserved` reads the keys. This is no list of released versions: numbering reads those only from the - catalog. Why: the operator, 2026-11-10 (a version reserved when the build starts); the key and the gap are the - design's choice. How it is checked: whole (builder), two builds close together reserve two versions, and the key - goes on `version-released`. + `state` section `reserved` one key per module and `MAJOR.MINOR`, the version its build reserved and each artifact's + digest once built, written by compare-and-set at the build's start; a newer build of the module takes the next after + the higher of the highest released and the one reserved, so two builds never share a version, and a superseded + build's version is never released, leaving a gap in the patches. The builder deletes the key when the catalog says + `version-released` for it, or when a version of another `MAJOR.MINOR` of the module is released (the manifest moved + on); `builder_reserved` reads the keys and `builder_reserved_drop` drops one on a person's word, written in the + hand-act log. A reserved version whose build ended with an artifact unpublished past the publish's 10-minute bound + is the builder's finding `version-unreleased`, naming the module, the version and the missing artifacts, cleared by + its `version-released`. `builder_publish_again` names the reserved version it publishes under. The builder reserves + nothing until the catalog says `versions-filled` (§372); a build waiting for it says `wait-held` naming the fill. The + seat `mesh-builds`' verb `reserved` answers a module's reserved version with each artifact's digest, its callers the + holders of the registry seats (design 51, *Publishing*). This is no list of released versions: numbering reads those + only from the catalog. Why: the operator, 2026-11-10 (a version reserved when the build starts); the key, the gap, + the finding and the verb are the design's choices. How it is checked: whole (builder), two builds close together + reserve two versions, the key goes on `version-released`, an unpublished artifact raises `version-unreleased`, and a + builder started before the fill reserves nothing until `versions-filled`. - §370 **The version reaches every artifact's build.** The build seat's ask carries the version, and the build module passes it to the artifact's build as the build argument `MESH_VERSION`: a Go package is tagged `vMAJOR.MINOR.PATCH`, an npm package's version is it, an image carries it as its tag and label, and a program reads it as its own version. A Go package's version is its tag: this departs from ADR 0573 §2, which has a Go package's version be its trunk - commit, by the operator's choice of 2026-11-10, and a later research brings that record in line. How it is checked: - whole (Go and TypeScript build modules), a built package and program carry the reserved version. + commit, by the operator's choice of 2026-11-10, and a later research brings that record in line. The git tag + (`go/vMAJOR.MINOR.PATCH`, design 51) is written by the builder only on the catalog's `version-released`, through the + seat `git`'s verb `tag`, whose callers are the builder alone, so a superseded build's reserved version is never + tagged. How it is checked: whole (Go and TypeScript build modules), a built package and program carry the reserved + version; whole (builder), the tag is written on `version-released` and never for a superseded build. - §371 **Only the first watched branch releases.** Only a build of the registration's first watched branch reserves and releases a version; a build of another watched branch reserves none, publishes no version and is delivered by its build, its fingerprint, as every want names one. Why: the operator, 2026-11-10. How it is checked: whole (builder), a `beta` build reserves nothing and is delivered. - §372 **The catalog's versions start from what is published.** At the new indexer's first start, `versions` is filled - once, as released, from each name's highest `published` version the builder's state `versions` held, read by - `builder_versions`; it is not filled again. Why: numbering reads only the catalog's released versions (§348), and - without this every first version under G would start below today's and Go's `@latest` would never move (the - design's choice, 2026-11-10). How it is checked: whole (indexer, builder), with `0.1.14` published before G, the - first G build of `0.1` is `0.1.15`. + once, as released, for each module from the highest `published` version over every artifact of the module that the + builder's state `versions` held (for mesh-sdk the higher of Go's `v0.1.14` and npm's `0.1.18`, so `0.1.18`), read by + `builder_versions` as a kept call (ADR 0668 §1, §2), an unanswered call raising `call-unanswered` after five minutes; + it is not filled again. Then the indexer says `versions-filled` on its seat, once, kept on its stream with no age + limit, and the builder reserves nothing before it (§369). The builder keeps serving `builder_versions` until then: the + running builder serves it already, and the new one serves it too, so no ordering edge is needed. The builder's state + `versions`, the floor and its tool `builder_floor_again`, the event `numbered`, `builder_versions`, and `tag-asks` are + removed in the first change after the first pipeline after G's delivery has delivered a change end to end, the state + through `drop` on a person's word (ADR 0595 §4). Why: numbering reads only the catalog's released versions (§348), + and without this every first version under G would start below today's and Go's `@latest` would never move (the design's choice, round 2 of the review by architectural domain, 2026-11-10). + How it is checked: whole (indexer, builder), with Go `v0.1.14` and npm `0.1.18` published before G, the first G build + of `0.1` is `0.1.19`, and a builder started before the indexer reserves nothing until `versions-filled`. - §373 **A group's run on request publishes no version.** A run on request of a group builds each member that - depends on a package member against that member's build by its digest: the build module fetches the package - member's build from the store by the digest its build's done key names and lays it in the build's workspace as the - module that package names (a Go workspace's replacement, a TypeScript local package), so nothing is fetched by a - version, and no version is reserved, numbered or published for the run. The tagged versions, `tag-wanted` (the - checker's, and none of the runner's) and the builder's `tag-asks` with its tools `builder_tag`, `builder_tag_asks` - and `builder_untag` go at G's delivery (design 51, *Tagged versions*). Why: the operator, 2026-11-10 about 21:40 CET, - no custom versions for now, a tagged version being one; a run delivers nothing (ADR 0666 §18), so it needs no - version. How it is checked: test 103. + depends on a package member against that member's build by its digest. The builder's build ask for such a member + carries each package member's build digest, which the builder takes from its own `build.ended` of that member's + build, its own fact (ADR 0596 §2); the build module fetches that build from the store by the digest, checks the + fetched bytes against it, and lays it in the build's workspace as the module that package names (a Go workspace's + replacement, a TypeScript local package), so nothing is fetched by a version, and no version is reserved, numbered + or published for the run. A dependent whose package member's build is not done waits, said as the builder's + `wait-held` naming the member, within building's 60-minute bound; a member's build that failed or is missing past + that bound fails the dependent's step, naming the member. The tagged versions, `tag-wanted` (the checker's, and none + of the runner's) and the builder's `tag-asks` with its tools `builder_tag`, `builder_tag_asks` and `builder_untag` go + at G's delivery (design 51, *Tagged versions*). Why: the operator, 2026-11-10 about 21:40 CET, no custom versions for + now, a tagged version being one; a run delivers nothing (ADR 0666 §18), so it needs no version; the digest's source + and the wait are the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: test 103. - §374 **A release records, per edge, the part it was built against or delivered after.** One version per module stays (§52), and an edge is judged by the digest of the part it points to. The catalog records on each released version (§349), in its key of `versions`, one entry per catalog-graph edge of the module's artifacts (the six kinds @@ -352,52 +386,66 @@ record it follows from, and replaces the builder's own choice where one was made and the builder carries them in each artifact's `package-published`, so the indexer reads no other part's state (ADR 0596). Kind 3 points at the toolchain part the agreed toolchain names (ADR 0584 §1), never at the build module's own program. - - kinds 2 and 4 (delivered after): the dependency's version and part digest the catalog last said - `version-delivered` for when the release is delivered (§376), one entry for the mesh. + - kinds 2 and 4 (delivered after): the dependency's version and part digest of its newest `version-delivered` + when this release is itself first delivered (its own `version-delivered`, §376), one entry for the mesh. - kind 5 (has acted for): a fact, not a version: the acting part, its step, the build that acted and that the act was done (or released by hand), from the holder's `wait-ended` (*Each step's wait is its own part's*). - kind 6 (runs a version that serves): a fact: the verb or manifest word used and the dependency's delivered - version that serves it. + version that serves it, written at the same first delivery. + + The entries of kinds 2, 4 and 6 read `not yet delivered` until that first delivery, are written once then, and none + is changed after; kinds 1, 3 and 5 are written at the release. A build of a watched branch other than the registration's first releases no version (§371): its entries are kept in the indexer's old `state` section `build-edges`, one key per module and watched branch holding the newest build's entries, keyed by its fingerprint, replaced by the next build and deleted when `registration-changed` says the branch is no longer watched, and they compare by the part's digest alike. The declared edge stays in the catalog - graph; an entry is the release's or the build's fact, never changed after. Why: the operator, 2026-11-10 about 22:30 to 23:00 CET and 22:10 CET (issue 1961), and + graph; an entry is the release's or the build's fact, written once. Why: the operator, 2026-11-10 about 22:30 to 23:00 CET and 22:10 CET (issue 1961), and the lead's review of these lines. Each entry is about 0.1 KiB, about ten a version, so `versions` stays within its 64 MiB (an estimate). How it is checked: test 104. - §375 **One query finds every edge behind, by the part's digest.** The seat `mesh-catalog`'s verb `outdated`, with the indexer's tool `indexer_outdated`, answers every entry that is behind: for kind 1, the recorded digest differs - from the part's digest in the dependency's newest released version (for a build of another branch, its newest - build on that branch); for kind 3, it differs from the toolchain part the agreed toolchain names now; for kinds 2 and - 4, it differs from the dependency's newest delivered version; for kind 5, the newest version of the acting part has - not acted for the dependent's current build; for kind 6, the delivered version no longer serves the verb or word. A - dependency's newer version that left the part's digest as it was makes nothing behind, so nothing is done twice for - the same inputs. Only a stale edge (§378) that is behind has an act that catches it up, a rebuild; an ordering edge - behind is shown and starts nothing. Each answer names the module and its version or build, the kind, the - dependency, what was recorded and what is newest, and the act. Its callers are any person, an agent session, the - healers, the runner and the assigner; `indexer_outdated_again`, a hand act written in the hand-act log, says the - event that starts a stale entry's rebuild again. At its start, the runner asks `outdated` once and starts the - rebuilds it names, so a restart or a missed event leaves nothing behind. Why: the operator, 2026-11-10 (issue - 1961's "outdated"). How it is checked: test 105. -- §376 **A stale edge rebuilds on its event; a delivery is waited for mesh-wide.** On a dependency's `version-released`, - the dependents by kind 1 whose entry is behind are rebuilt against it and their pipelines started, as on - `package-published` (§53); when the agreed toolchain names a new toolchain part, the dependents by kind 3 whose entry - is behind are rebuilt. No other edge rebuilds or delivers anything again. A dependent's new version that a change - orders after its dependency's new version by kind 2, 4 or 6 is wanted only once that dependency's version is - delivered to every machine that places it and is present: the catalog says `version-delivered` (the module, the - version and its parts' digests) once every present machine placing it runs it healthy, from the node-engine seat's - `running`, on `mesh.seat.mesh-catalog.event.version-delivered`, kept 90 days at 16 MiB, and the assigner writes the - dependent's wants on it. A machine away is named and catches up when it is back (ADR 0603 §7); meanwhile the - dependent's status, in `assigner_why_not` and the status view, reads "waiting for delivery of ", naming - the machines it waits for. When every machine that places the dependency is away, it is not delivered: its status - reads "waiting for to be back" and its dependents wait; nothing is raised for a machine simply away. - Kind 5 is held by the acting parts' waits (*Within a revision the new version acts first*). Why: the operator, 2026-11-10 about 22:30 to 23:00 CET. How it is - checked: tests 104 and 106. + from the part's digest in the dependency's newest released version (for a build of another branch, its newest build + on that branch); for kind 3, it differs from the toolchain part of the builder's newest `toolchain-agreed`; for kinds + 2 and 4, it differs from the dependency's newest `version-delivered`; for kind 5, the newest version of the acting + part has not acted for the dependent's current build; for kind 6, the delivered version no longer serves the verb or + word. A dependency's newer version that left the part's digest as it was makes nothing behind, so nothing is done + twice for the same inputs. Only a stale edge (§378) that is behind has an act, a rebuild; an ordering edge behind is + shown and starts nothing. Each answer names the module and its version or build, the kind, the dependency with its + watched branch and source revision, what was recorded and what is newest, and the act. It may be asked for one + dependency. Its callers are any person, an agent session, the healers and the runner. At its start the runner asks + `outdated` once, a kept call raising `call-unanswered` after five minutes, and starts the rebuilds it names; the + runner's tool `runner_catch_up`, for the operator and the healers and written in the hand-act log, does the same by + hand, for one dependency or all. A restart or a missed event so leaves nothing behind. Why: the operator, 2026-11-10 + (issue 1961's "outdated"); `runner_catch_up` replaces the indexer's hand tool, which would have had to say another + part's event (the design's choice, round 2 of the review by architectural domain, 2026-11-10). How it is checked: test 105. +- §376 **A stale edge rebuilds on its event; a delivery is waited for mesh-wide.** The rebuilds start on two events + the runner consumes: kind 1 on `package-published` (§53), and kind 3 on `toolchain-agreed`, which the builder says + on its seat `mesh-builds` when the agreed toolchain of a language changes (ADR 0584 §1), naming the language, the + version and the toolchain part's digest; for each, the runner asks `outdated` for that dependency and starts a + pipeline for each dependent behind. `version-released` starts no rebuild. No other edge rebuilds or delivers anything + again. A dependent's new version that a change orders after its dependency's new version by kind 2, 4 or 6 is wanted + only once that dependency's version is delivered to every machine that places it and is present. The assigner judges + it, since placement is its own state: from its placements, the node-engine seat's `running` events and the + watchman's presence of each node, judged again on each of them and on each placement change, and says + `version-delivered` (the module, the version, its parts' digests and the machines away) on its seat `mesh-wants` + (`mesh.seat.mesh-wants.event.version-delivered`) once every present machine placing it runs it healthy; the catalog + hears it for its entries (§374), and the assigner writes the dependent's wants on it. The dependent's wait is the + assigner's, said `wait-held` with a 60-minute bound from the dependency's `version-released` (*Each step's wait is its + own part's*): past it, the watchman's finding `delivery-waits` names the dependency, the version and the machines not + running it, with each one's node-engine version, so a node-engine from before G that never runs it is named. A + machine away is named and catches up when it is back (ADR 0603 §7); meanwhile the dependent's status, in + `assigner_why_not` and the status view, reads "waiting for delivery of ", naming the machines it waits for. + When every machine that places the dependency is away, it is not delivered: its status reads "waiting for + to be back", its dependents wait, and nothing is raised for a machine simply away, the bound counting only while a + placing machine is present. `assigner_delivered_again`, for the operator and the healers and written in the + hand-act log, says `version-delivered` again for one version when it holds. Kind 5 is held by the acting parts' waits + (*Within a revision the new version acts first*). Why: the operator, 2026-11-10 about 22:30 to 23:00 CET; who judges it, the event's seat, the bound and the tools are + the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: tests 104 and 106. - §377 **The first fill.** At the indexer's first start under G, after `versions` is filled (§372), each module's newest version gets its entries once: kinds 1 and 3 from the dependency version published latest before that version's build was claimed, read with the times of `builder_versions`, and its part's digest; kinds 2, 4 and 6 from - the versions every present machine runs, read from the last `running` of each module on each node, which the + the versions every machine placing it runs, present or away, read from the last `running` of each module on each node, which the node-engine seat's stream keeps last per subject with no age limit; kind 5 as done for every acting part's running build. Each filled entry is marked `filled`, so a person sees it was derived, not recorded. It is not filled again. Why: so the first `outdated` does not find the whole mesh behind (the lead, 2026-11-10). How it is checked: whole @@ -410,11 +458,26 @@ record it follows from, and replaces the builder's own choice where one was made - §379 **A want names the released version and each part's digest.** From G's delivery a want `want..` names, beside its fingerprint and source revision, the module's released version, for people (none for a build of another watched branch), and the digest of each of its parts; the node-engine fetches and checks each part by its - digest, never by the version. The node-engine of G reads the two fields, its reader edge of kind 4 delivered before - any want writes them (ADR 0667 §4, §9). Why: the operator, 2026-11-10 about 22:30 to 23:00 CET. How it is checked: whole (assigner, node-engine), a want names - the version and the parts' digests, and a fetched part whose digest differs is not applied. + digest, never by the version. The assigner takes the digests from `version-released` for a released version, and from + the builder's `build.ended`, which carries each artifact's digest and, for a kept build, its keeping node, for a build + of another watched branch and for a kept build (§271); never from the builder's done key (ADR 0596 §2). It writes the + two fields into a node's want only once that node runs G's node-engine, as it holds a node's other wants for its new + node-engine (*Composing waits per node*), so the reader edge holds per node and a node away on an older node-engine + gets its node-engine's want first. Why: the operator, 2026-11-10 about 22:30 to 23:00 CET; the sources and the edge per node are the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: + whole (assigner, node-engine), a want names the version and the parts' digests taken from those events, a node on an + older node-engine gets no new field, and a fetched part whose digest differs is not applied. - §380 **`indexer_edges` answers both directions.** For any module or part, per watched branch, `indexer_edges` answers what it depends on and what depends on it; per edge its kind, stale or order (§378), the manifest word it came from, the part it points to, and what its newest release recorded (version and digest, or the fact for kinds 5 - and 6), and whether it is behind now (§375). Why: the operator, 2026-11-10 about 22:30 to 23:00 CET. How it is checked: whole (indexer), a fixture module's + and 6), and whether it is behind now (§375). Its callers are any person, an agent session and the healers. Why: the operator, 2026-11-10 about 22:30 to 23:00 CET. How it is checked: whole (indexer), a fixture module's edges are answered from both ends with their newest release's record. +- §381 **A registry holder checks a publish against the reservation.** From G's delivery the holder of a registry + seat asks the seat `mesh-builds`' verb `reserved` (§369), in place of `builder_versions`, whether the module, the + version and the artifact's digest are reserved, and refuses loudly otherwise (ADR 0605 §4); a version the catalog + already holds released for that digest is answered as held and stores nothing. Why: ADR 0605 §4 has the holder + refuse a version the builder did not give, and the builder's list goes (§372); the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: whole + (registry holder, builder), a publish of an unreserved version is refused, naming it. +- §382 **An older version released after a newer starts nothing.** When every artifact of a version lower than the + module's highest released is published late (a retry of an older reservation), the catalog records it `released + late`, says no `version-released` for it, and the runner starts no rebuild on its `package-published` (§53). Why: + a rebuild against an older part would move a dependent back; the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: test 102. diff --git a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md index f33090d2..44f758a2 100644 --- a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md +++ b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md @@ -153,7 +153,9 @@ the verb. match its digest. At 80 % of the cap it writes a finding (cleared below 80 %); at the cap it refuses a new transfer with `refused` naming the cap. Its tool `registry_incoming` lists open transfers and the fill, and drops one by hand. - **The holder asks the builder**, through `builder_versions`, whether the name, version and digest are recorded, and - refuses loudly otherwise (ADR 0605 §4). A rebuild of a held fingerprint answers the held version and stores nothing. + refuses loudly otherwise (ADR 0605 §4). A rebuild of a held fingerprint answers the held version and stores nothing. From the delivery of + design 47's milestone G the holder asks the seat `mesh-builds`' verb `reserved` instead, whether the module, the + reserved version and the artifact's digest are reserved, and refuses loudly otherwise (design 47 G §381). - **How a publish ends**: the holder emits `published` or `refused`. The build module waits for one of them, matching on its transfer, name and version, at most 10 minutes, narrating "waiting on " at least every five minutes; past the bound it writes the key refused, -- 2.54.0 From 9bcd0ad1976cd96b2cc95bd5b5db7a08f9240c4f Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 10 Nov 2026 22:43:38 +0100 Subject: [PATCH 10/13] Drop the lint scan for fixed names and keep "no part fixes a name" as a guideline the code review reads The operator, 2026-11-11: the scan was a flaky check, and the rule is a guideline, not a decision. The names G's own work removes are removed by its subtasks; the exemption line and the scan's checks go with it. Claude-Session: https://claude.ai/code/session_01JG73KK8HD1gufnqyFxMFdW --- .../47-delivery-from-commit-to-delivered.md | 7 +++--- .../G-1-pipelines-registration-and-steps.md | 13 +++++----- .../G-5-acting-parts-check-and-kept-data.md | 3 ++- .../47-milestones/G-6-verbs-and-retires.md | 4 +-- .../01-to-be/47-milestones/G-7-answers.md | 25 ++++++++----------- 5 files changed, 25 insertions(+), 27 deletions(-) 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 3c170f47..41f2695f 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 @@ -1622,7 +1622,7 @@ list: that group's own heads, read from the check's record (ADR 0644, How it is checked), and no member of it was delivered ahead of it. 27. **A new acting part needs no code elsewhere.** A fixture seat declaring a rule, with a fixture holder, gets its - edges with no code change elsewhere, and a search of the delivery parts' code finds no other module's name. + edges with no code change elsewhere; the code review reads that no delivery part's code names another module. 28. **Waits stay within their step.** A cycle among waits within one step is refused; an acting part acting on itself, and two acting parts acting on each other in different steps, pass. 29. **A failed acting part leaves the mesh on what runs.** A build module whose new build fails, and one whose wait @@ -1820,9 +1820,8 @@ milestone with more tests than it starts with (research 085, Direction, One mile prerequisite; with one member passed and another still running no want is written until `group-ended` (whole, indexer, gate, runner and assigner, mesh-delivery and mesh-catalog; live). 68. **R6, modules, never repositories.** A module whose watched branch is `beta` on one machine is delivered from it - there only; the lint step refuses a module, branch or repository name in a string, a constant or a table, and not - an import path (whole, assigner, mesh-delivery; unit, the Go build module's lint, mesh-catalog; live, a search of the - delivery parts' code). + there only (whole, assigner, mesh-delivery); the code review reads that no part fixes a module, branch or repository + name in its code, a guideline (design 47 G §7). 69. **R7, any size.** Values of one, ten and a hundred times the bus's per-value limit are stored, sent as a verb's reply and as an event, and read back whole by a caller the sender's seat definition names and a consumer declaring its event, and refused to a module that declares neither (whole, mesh-sdk, against the real bus). diff --git a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md index 0e470faa..edbd1744 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md +++ b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md @@ -92,10 +92,12 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l list when it is removed (*How milestone G itself is checked and delivered*). A registration holds no foundation value. How it is checked: a unit test of the manifest validator refuses a template in the manifest of every part on its list, the runner's included, and judges every other part's manifest as any part's. -- §7 **No part fixes a name.** Every branch, repository and module name a part needs is read from the registrations and the - catalog graph; the lint step refuses such a name in a string, a constant or a table of code logic, a Go import path - not being code logic (ADR 0666 §5). About 39 such names in 9 parts were found on 2026-11-09 (research 086, Findings); - milestone G removes them. +- §7 **No part fixes a name: a guideline the code review reads.** Every branch, repository and module name a part + needs is read from the registrations and the catalog graph, never fixed in its code. This is a guideline, not a + step: the code review reads it, and no lint step scans for names (the operator, 2026-11-11 about 00:05 CET: the scan was a flaky check; where + guidelines live is research 087's). It departs from ADR 0666 §5's refusal by the lint step, by the operator's choice, + and a later record brings it in line. The names G's own work removes (about 39 in 9 parts, found on 2026-11-09, + research 086, Findings) are removed by its subtasks, each naming the name it removes. - §8 **The first registrations are one ask.** At its first start on G's build the indexer, holding no registration, proposes one registration for every module its trunk revision holds, from what it took in (the origin and path of each), its watched branch the repository's default branch read through the seat `git`, and the statuses the check @@ -123,8 +125,7 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l the warrant writes nothing, and a declined one writes nothing; a change from `required` to `bypass` asks, showing that line; `registration-changed` is said and `registrations` answers the history; the first start proposes the seed as one ask, and `indexer_seed_again` asks it again after a Decline and after an expiry; a policy making code review - `required` is refused. Unit (the Go build module's lint step): a module name in a string, a constant and a table is - refused, an import path is not. Tests 75, 68 and 83. + `required` is refused. Tests 75, 68 and 83. ### Watched branches and the catalog graph per branch diff --git a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md index 8e84e5c9..64ca5ec3 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md +++ b/03-DESIGN/01-to-be/47-milestones/G-5-acting-parts-check-and-kept-data.md @@ -197,7 +197,8 @@ names go, since every new acting part needed code changes elsewhere (research 08 (graph): an acting part acting on itself, two acting parts acting on each other in different steps, and a cycle through a build module's edge each pass the cycle check, each new version first in its own step; a cycle among waits within one step is refused. Unit (assigner): the Approve ask of a change that adds an acting rule, and of one - that widens one, names the rule. Live: a search of the delivery parts' code finds no other module's name. + that widens one, names the rule. The code review reads that no delivery part's code names + another module (§7). ## Nothing merges into a repository under check: removed diff --git a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md index b37d2d16..8e88fddd 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md +++ b/03-DESIGN/01-to-be/47-milestones/G-6-verbs-and-retires.md @@ -201,7 +201,7 @@ events are subscribed to): a new event of the seat `knowledge-base` would be a s after the first pipeline after G's delivery has delivered a change end to end and on the operator's one word, its buckets through `drop` after a snapshot whose restore is tested (ADR 0667 §12); the bundle of steps run before a merge; the Approve per merge, the milestone ask and its three events, and the gate's merges; the gate's merge states, one key per pull request and its verbs `table`, `recheck` and - `close`; the trunk-only want; `source.main`, for one source revision per watched branch; every fixed branch, - repository and module name in code logic; the recovery merge verb this design sketched, never built; and the holders' + `close`; the trunk-only want; `source.main`, for one source revision per watched branch; the fixed branch, + repository and module names G's subtasks remove (§7); the recovery merge verb this design sketched, never built; and the holders' trusted `callers` values, for the seat definitions' `callers`. The words **merge check**, **try**, **trunk commit** and **merge turn** go to *Not:* lines with the as-is update after that delivery, as the glossary's entries say. diff --git a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md index c3e1df89..1e6f627f 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -71,16 +71,13 @@ record it follows from, and replaces the builder's own choice where one was made build modules import; none keeps a copy. Why: code shared between modules lives in an SDK, and the delivery domain's shared code is `mesh-delivery-sdk`'s (ADR 0635 §4); the copies were met in develop. How it is checked: a search of the three build modules finds no fetch, release or step-run function of their own. -- §328 **Lint per language.** Go's lint step is the Go build module's linter with the fixed-names rule; TypeScript's is the - type-check with the fixed-names rule; an image's lint is the fixed-names rule over its build files; - an image has no unit or integration test step, so `steps` answers none for it and the seed has those `bypass` - (*A module's registration*). Why: ADR 0666 §5 has the lint step refuse fixed names in every language, and §7 lets a - build seat answer only the steps an artifact can run. How it is checked: whole (TypeScript and image build modules), - a fixture naming a module in code fails lint. -- §329 **What the fixed-names rule exempts.** It exempts a Go import path, a comment and a test file (`_test.go`, - `*.test.ts`), never a struct tag or any other string of code. Why: none of the three is code logic (ADR 0666 §5), - and a test fixture names modules. How it is checked: unit (lint rule), a name in a comment and in a test file - passes, in a struct tag fails. +- §328 **Lint per language.** Go's lint step is the Go build module's linter; TypeScript's is the type-check; an image + has no lint, unit or integration test step, so `steps` answers none for it and the seed has those `bypass` (*A + module's registration*). No lint step scans for module, branch or repository names (§7). Why: ADR 0666 §7 lets a + build seat answer only the steps an artifact can run; the scan's removal is the operator, 2026-11-11 about 00:05 CET. How it is checked: whole + (TypeScript and image build modules), `steps` answers the type-check for a TypeScript artifact and nothing for an + image. +- §329 **Withdrawn.** It named what the fixed-names scan exempted; the scan goes (§7, the operator, 2026-11-11 about 00:05 CET). - §330 **The runner's own kept data age by its own deletes, in the step log's order.** The runner deletes its own `pipelines` keys, `kept` entries and `step-outputs` objects by the retentions of *The runner's kept data*. It keeps, in its old `state` section `aged`, three step-log sequences, one per retention (7, 30 and 90 days), each the last @@ -121,8 +118,8 @@ record it follows from, and replaces the builder's own choice where one was made fixtures. - §333 **The runner hears the indexer's own event.** The runner's manifest `consumes` the indexer's own event `take-in.taken` (ADR 0666 §13), which ADR 0596 §1 lets any module subscribe to; the indexer's name stands in that - manifest only, never in the runner's code. Why: ADR 0666 §13, in force, names that event. How it is checked: a search - of the runner's code finds no module name (lint). + manifest only, never in the runner's code. Why: ADR 0666 §13, in force, names that event. How it is checked: the code review + reads that no module name stands in the runner's code (§7). - §334 **A publication's pipelines and their key.** `package-published` carries the package's artifact reference (`:`, from the catalog entry), its version and digest, the watched branch and the source revision its build was made from. The runner reads the dependents from that revision's catalog graph, the artifacts with a @@ -287,14 +284,14 @@ record it follows from, and replaces the builder's own choice where one was made - §364 **The node-engine claims its seat.** The node-engine's manifest claims the node seat `node-engine` (ADR 0563), and the indexer and the node-engine's own guard read its holder from the seat list, keeping no module name. How it is - checked: lint finds no module name in either. + checked: the code review reads that neither fixes a module name (§7). - §365 **The condition keeper reads its sources from the catalog.** The catalog graph entry carries each kept data a manifest declares (its name and its section or template); the keeper takes a first view of the catalog half and reads a module's `findings` wherever its entry declares a state `findings`, and the node-engine's holder from the seat list. It joins the readers of *Every other reader of which modules exist*, reading every watched branch's newest catalog revision, so a module watched on any branch is a source. A condition of a module that is in no branch's revision is cleared, said with the reason `module left the catalog`. Before its first view the keeper keeps raising from the - sources it held, and says `view-waits` after five minutes (*A part with no view says it waits*). Why: no fixed names (ADR 0666 §5), + sources it held, and says `view-waits` after five minutes (*A part with no view says it waits*). Why: no part fixes a name (§7), and the keeper reads findings (ADR 0564 §2). How it is checked: whole (keeper), a fixture module declaring `findings` is read with no code change. -- 2.54.0 From 9f9cb58f54ea28619d6f3e48f3b07849b8730783 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 02:20:24 +0100 Subject: [PATCH 11/13] Write the operator's answers into milestone G's part: findings heard as events, invokers chosen, the shared account a known limit The condition keeper read other modules' findings states, which only their owner may reach; a node-engine restarted with the full seat list never cleared its own finding, because only its process remembered writing it. Observers now say each finding as an event and state all they own at each start and on the keeper's ask, so the keeper's view is rebuilt from events alone. The `invokers` form is the operator's choice, and the tool runner's shared account stays as it is in G, named as a known limit. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- 00-META/glossary.md | 8 +- .../G-2-pipelines-sizes-checks-and-tools.md | 4 +- .../01-to-be/47-milestones/G-7-answers.md | 108 +++++++++++++++--- 03-DESIGN/01-to-be/47-milestones/G.md | 4 +- 4 files changed, 103 insertions(+), 21 deletions(-) diff --git a/00-META/glossary.md b/00-META/glossary.md index b6b163de..2b846b32 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -916,12 +916,14 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564, 0668. (ADR 0283). - **probe** — one look at one thing's health, run by whoever owns the verdict; what a probe returns is a **finding**, before it becomes a condition. A finding is kept as a key `..` in its observer's - bucket `findings` until it is no longer observed (ADR 0564 §2). + bucket `findings` until it is no longer observed (ADR 0564 §2); from design 47's milestone G the observer also says + each as its own event, and states every finding it owns at its start, so the condition keeper hears findings and + reads no observer's bucket (ADR 0596 §2; design 47 G §383, §384). - **node health report** — each node-engine's own report of its node's health, a key that node's engine writes and the watchman reads (ADR 0564, as its progressive insight of 2026-10-29 has it); its layout is mesh-sdk's. - **observer** — a part that writes findings for the condition keeper: the watchman, the self-check and the - healers (ADR 0564 §2), and each caller of a kept call, for its `call-unanswered` (ADR 0668 §3). An observer never - writes a condition. + healers (ADR 0564 §2), and each caller of a kept call, for its `call-unanswered` (ADR 0668 §3); from design 47's + milestone G, every module that writes findings (design 47 G §383). An observer never writes a condition. - **condition keeper** — the holder of the seat `mesh-conditions`: the one writer of the open conditions, raising each from an observer's finding and clearing it when the finding goes, and saying `condition-raised`, `condition-changed` and `condition-cleared` (ADR 0564 §2, §3). The controller keeps them diff --git a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md index 8d4b576d..efb17334 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md +++ b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md @@ -3,7 +3,7 @@ layer: to-be status: in-progress design: 47 milestone: G -updated: 2026-11-10 +updated: 2026-11-11 decisions: - "ADR 0667" - "ADR 0666" @@ -229,7 +229,7 @@ and an estimated 2 mesh-sdk publishes a day, each rebuilding about 120 artifacts | the builder | `reserved`: one key per module and `MAJOR.MINOR` (§369) | old `state` section | until its `version-released`, a release of another `MAJOR.MINOR` of the module, or `builder_reserved_drop` | a few KiB; at the cap the builder reserves nothing and says why | | the assigner | `written`: the last source revision and end count per module and watched branch (§352) | old `state` section | until the branch is unwatched | a few KiB | | the indexer and the assigner | `inputs`: a `run` ask's input by write time and sha256 (§354) | old `state` section | until the ask's `step-ended`, at most a day | 64 MiB; an estimated 300 asks a day of under 100 KiB each is under 30 MiB; at the cap the asker refuses the ask loudly, naming the store | -| the runner and the assigner | `findings` (§355) | old `state` section | an entry deleted when its finding clears | a few KiB | +| the runner and the assigner | `findings` (§355), read by their own read tools alone; the keeper hears the findings as events (§383) | old `state` section | an entry deleted when its finding clears | a few KiB | | the runner | `aged`: three step-log sequences (§330) | old `state` section | never deleted | bytes | | the teller | `widens-said` (§335) | the template `state` | until the branch is unwatched | a few KiB; the template's | | each build module | `steps` (§326) | the template `state` | one key per artifact, dropped by `steps` with `drop` | under 1 MiB; the template's | diff --git a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md index 1e6f627f..05d2dfc2 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -3,12 +3,13 @@ layer: to-be status: in-progress design: 47 milestone: G -updated: 2026-11-10 +updated: 2026-11-11 decisions: - "ADR 0668" - "ADR 0667" - "ADR 0666" - "ADR 0656" + - "ADR 0636" - "ADR 0635" - "ADR 0625" - "ADR 0619" @@ -21,6 +22,8 @@ decisions: - "ADR 0563" - "ADR 0548" - "ADR 0363" + - "ADR 0289" + - "ADR 0253" --- # 47, milestone G: the answers to the builders', the reviewers' and the operator's questions @@ -147,7 +150,9 @@ record it follows from, and replaces the builder's own choice where one was made `holder:@same-node` (the holder of that seat on the node of the verb's holder). A definition whose `callers` today is text is given a list in this milestone: "any caller whose role grants it" becomes `invokers`, "a person" `persons`, and every other text the names the design gives that verb. Why: ADR 0666 §20 makes the definition the - grants part's one source of caller limits, in a form it reads. How it is checked: unit (definition reader), each + grants part's one source of caller limits, in a form it reads; `invokers`, which ADR 0666 §20's list does not name, + is the operator's choice for the verbs whose text said "any caller whose role grants it" (Telegram, 2026-11-11 02:13 + CET: "Any declaring module"). How it is checked: unit (definition reader), each form accepted and any other entry refused; the grants part's table test grants each form. - §337 **The bus module's restore points.** The verbs the holder of `node-backup` on the bus module's node calls to take and restore a module's kept data on the bus name `holder:node-backup@same-node`. Why: only that holder takes them @@ -251,10 +256,12 @@ record it follows from, and replaces the builder's own choice where one was made composition step runs on parts built from the heads, in the sandbox (ADR 0666 §17). How it is checked: whole (assigner), a composition step's `run` is answered and its input read by digest. - §355 **The assigner says an unanswered `manifest-reads`.** The assigner, the one caller of the node-engine's - `manifest-reads` from G, writes `call-unanswered` into its own findings state, and the condition keeper's reads gain - it in the same change (ADR 0668 §3). The assigner and the runner each declare that state, `findings`, in their old - `state` section, both being foundation parts. How it is checked: whole (assigner, runner, condition keeper), a node-engine - that never answers raises `call-unanswered` after five minutes, and the keeper raises it. + `manifest-reads` from G, writes `call-unanswered` as its own finding and says it as an observer does (§383, §384), + so the condition keeper hears it with no change of its own (ADR 0668 §3; the operator, 2026-11-11). The assigner and + the runner each declare their own state `findings` in their old `state` section, both being foundation parts, read + by their own read tools alone. How it is checked: whole (assigner, runner, condition keeper), a node-engine that + never answers makes the assigner say `finding-written` for `call-unanswered` after five minutes, and the keeper + raises it from that event. - §356 **Two acting parts in different steps make no cycle.** The assigner's cycle check counts waits within one step only (*A wait holds only within its step*). How it is checked: the unit rows there. - §357 **Which provider a requirement points to.** In the assignment graph, by design 27's order: the named seat's @@ -285,15 +292,17 @@ record it follows from, and replaces the builder's own choice where one was made - §364 **The node-engine claims its seat.** The node-engine's manifest claims the node seat `node-engine` (ADR 0563), and the indexer and the node-engine's own guard read its holder from the seat list, keeping no module name. How it is checked: the code review reads that neither fixes a module name (§7). -- §365 **The condition keeper reads its sources from the catalog.** The catalog graph entry carries each kept data a - manifest declares (its name and its section or template); the keeper takes a first view of the catalog half and - reads a module's `findings` wherever its entry declares a state `findings`, and the node-engine's holder from the - seat list. It joins the readers of *Every other reader of which modules exist*, reading every watched branch's newest catalog - revision, so a module watched on any branch is a source. A condition of a module that is in no branch's revision is - cleared, said with the reason `module left the catalog`. Before its first view the keeper keeps raising from the - sources it held, and says `view-waits` after five minutes (*A part with no view says it waits*). Why: no part fixes a name (§7), - and the keeper reads findings (ADR 0564 §2). How it is checked: whole (keeper), a fixture module declaring `findings` - is read with no code change. +- §365 **The condition keeper finds its sources in the catalog.** The catalog graph entry carries the events each + manifest declares in its `streams`; the keeper takes a first view of the catalog half, and its sources are the + modules whose entry declares the finding events of §383, the node-engine among them, its holder read from the seat + list. It joins the readers of *Every other reader of which modules exist*, reading every watched branch's newest + catalog revision, so a module watched on any branch is a source. A condition of a module that is in no branch's + revision is cleared, said with the reason `module left the catalog`. Before its first view the keeper keeps the + conditions it holds and applies no finding event, which waits on its durable consumer, and says `view-waits` after + five minutes (*A part with no view says it waits*). It reads no module's `findings` state (§383). Why: no part fixes + a name (§7), and the keeper hears findings and reads no other module's state (ADR 0596 §2, §4; the operator, + 2026-11-11). How it is checked: whole (keeper), a fixture module declaring the finding events is heard with no code + change; the catalog's validation finds no `findings` in the keeper's manifest `reads`. ### From round 1 of the review by architectural domain (2026-11-10) @@ -478,3 +487,72 @@ record it follows from, and replaces the builder's own choice where one was made module's highest released is published late (a retry of an older reservation), the catalog records it `released late`, says no `version-released` for it, and the runner starts no rebuild on its `package-published` (§53). Why: a rebuild against an older part would move a dependent back; the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: test 102. + +### The operator's answers to milestone G's design questions (2026-11-11) + +*ADR 0596 §1, §2, §4, ADR 0564 §2, §12, §13, ADR 0668 §1, §3, ADR 0636 §1, ADR 0666 §16, ADR 0363 §2. The operator, +2026-11-11, through lead-47: the condition keeper reads no other module's state and hears each module's findings as +events, and a module states its findings from its own truth; the shared tool-runner account stays as it is in G.* + +- §383 **An observer says its findings as events; the keeper reads no observer's state.** Every module that writes + findings (those the keeper reads today and those G adds: the watchman, the self-check, the healers, the runner, the + assigner, the indexer, the builder, the forge bridge and the node-engine among them) is an observer, and says each + change of a finding as an event of its own, declared in its manifest's `streams` and published by its account alone + on `mesh.stream..event..`, the node it runs on in the subject (§260, ADR 0363 §2): + `finding-written` when a finding is raised or what it says changes (only then, ADR 0289 §1, §4), and + `finding-cleared` when it no longer observes it. `finding-written` carries `observer` (the module), `node`, `scope`, + `id` and `kind` (the key `..` of ADR 0564 §2), `severity`, `subject`, `evidence`, ADR 0253's words + (`headline`, `explanation`), `since` and `at`; `finding-cleared` carries `observer`, `node`, `scope`, `id`, `kind`, + `at` and `why`. mesh-sdk's finding writer (`go/views`' `FindingWriter`) says them for every part, beside the key it + writes in the part's own state `findings`, which stays the owner's and is read only by the owner's read tool. The + keeper hears them on one durable consumer of its own: its manifest's `consumes` names them with the emitter `*` + (`*.finding-written`, `*.finding-cleared`, `*.findings-stated`), a form that takes that event from every module + whose manifest declares it, and the grants part grants the subscription per declaring module, derived from the + manifests as every grant is (§59); the manifest validator takes `*` only as the whole emitter before a declared event + name. The keeper takes a finding only from a source its catalog view names (§365), raises or changes a condition on + `finding-written` and clears it on `finding-cleared`, save while ADR 0564 §13 holds it; a condition's looks are + those of its last event, and the observer's read tool shows its newest. What exists today goes in the same change: + every `findings` entry of the keeper's manifest `reads`, its `consumes` of the assigner's and the indexer's + `views.findings` and its `invokes` of `assigner_read_findings` and `indexer_read_findings`; the assigner and the + indexer declare no `views.findings`, which no reader follows any more; the read tools stay for persons and sessions. + No ordering edge is needed: a source still on its old version says nothing, its conditions stand as they are, and + §384's bound names it. Why: only its owner reaches a module's state, and a part hears another's events (ADR 0596 §2, + §4); the operator, 2026-11-11. The subjects, the fields, the writer and the `*` emitter are the design's choice. + How it is checked: unit (mesh-sdk's writer), a finding written and one cleared each say their event with these + fields after the key; unit (manifest validator), `*.finding-written` accepted, `*.>` and `*` before an undeclared + event refused; whole (keeper), a fixture observer's `finding-written` raises a condition and its `finding-cleared` + clears it, and the same event from an account that is not the source's is refused by the bus; the catalog's + validation finds no `findings` in the keeper's `reads`. +- §384 **An observer states every finding it owns at its start and when the keeper asks; the keeper's view is + rebuilt from those events alone.** At each start, and on each ask of the keeper, an observer judges every finding it + owns anew from its own truth, the cause each kind watches (the seat list it holds, a call still unanswered, a wait + still standing), never from what a process remembers and never from its `findings` state, and says `findings-stated` + on `mesh.stream..event.findings-stated.`, carrying `observer`, `node`, `at`, `asked` (the ask it + answers, none at a start) and `findings`, every finding it observes now with `finding-written`'s fields; a finding + whose bound has not passed since the start (a wait of under five minutes) is not among them. The keeper sets its view + of that observer on that node to exactly that set: it raises what is new and clears each condition of that observer + and node the set does not name, with the reason `not stated at 's start` or `not stated when asked`; for an + observer whose catalog entry claims only seats held once for the mesh, the set replaces its findings of every node, + so a holder moved to another machine leaves none behind. The keeper asks with its seat's event `findings-asked` + (`mesh.seat.mesh-conditions.event.findings-asked`, carrying `ask`, `at`, `by` and, to ask one, `module`), declared in + the seat's definition and heard by every observer through mesh-sdk's writer, which declares it in the observer's + `consumes`. It asks after its first view (§365) at each of its starts, and on `resync` (ADR 0564 §12), which so reads + no state. An observer that states nothing within five minutes of an ask while its machine is present is the keeper's + own finding `findings-unstated`, naming the module and the node, cleared by its next `findings-stated`; its conditions + stand meanwhile, and an observer on a machine away is not named. A set larger than one bus message goes in the + observer's sent store (§59). The node-engine's `seat-list-unread` (ADR 0636 §1) is stated so: a node-engine started + with the full list states no `seat-list-unread`, and the keeper clears it at that start. Why: the node-engine's + seat-list waiter kept only in its process that it had written its finding, so a node-engine restarted with the full + list never cleared it, and two machines said they waited for the seat list for a day while both held it (issue 1966); + the operator, 2026-11-11: the fix is NATS-native, from each observer's own truth. The event, its fields, the bound, + the reasons and the replacement for an observer held once are the design's choice. How it is checked: whole (the + node-engine, and each observer through mesh-sdk's writer), a module restarted after raising a finding whose cause has + gone clears it at its start, the node-engine's `seat-list-unread` among them; whole (keeper), a `findings-asked` is + answered by a fixture observer's `findings-stated`, a condition the set does not name is cleared with its reason, and + an observer that states nothing raises `findings-unstated` after five minutes; unit (mesh-sdk's writer), a start + states only the findings observed now. +- §385 **One tool-runner account for every module of a machine stays as it is.** On a machine where one tool runner + serves every module's tools on one bus account, any tools bundle there may read any sent store (§59) that account is + granted, and call `merge-pull` or the runner's `run` as a session; G changes neither that account nor its grants, a + known limit. Why: the operator, 2026-11-11; modules running as the machine's account are issue 1966's (note 193), + whose fix carries its check. How it is checked: none in G; the limit is named on issue 1966. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index dc48292b..eb95d6e1 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -3,7 +3,7 @@ layer: to-be status: in-progress design: 47 milestone: G -updated: 2026-11-10 +updated: 2026-11-11 decisions: - "ADR 0668" - "ADR 0667" @@ -59,7 +59,9 @@ decisions: - "ADR 0398" - "ADR 0391" - "ADR 0363" + - "ADR 0289" - "ADR 0283" + - "ADR 0253" - "ADR 0240" - "ADR 0235" - "ADR 0233" -- 2.54.0 From bfd5fd2c0b2d272b7d2ad955712ad6cb60fc0324 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 02:21:43 +0100 Subject: [PATCH 12/13] Hear an event every declaring module says, keep the connectivity verbs open, and clear findings of a node a module left The keeper finds its sources in the catalog and names none in its manifest, so one generic consumes form with its validator rule and per-module grant carries the finding events; the operator's answer that a verb open today stays open covers the connectivity verbs that name no callers; and findings of a module no longer placed on a node would otherwise stand forever. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../01-to-be/47-milestones/G-7-answers.md | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md index 05d2dfc2..ba512476 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -152,7 +152,12 @@ record it follows from, and replaces the builder's own choice where one was made `persons`, and every other text the names the design gives that verb. Why: ADR 0666 §20 makes the definition the grants part's one source of caller limits, in a form it reads; `invokers`, which ADR 0666 §20's list does not name, is the operator's choice for the verbs whose text said "any caller whose role grants it" (Telegram, 2026-11-11 02:13 - CET: "Any declaring module"). How it is checked: unit (definition reader), each + CET: "Any declaring module"), and by the same answer, that a verb open today stays open to any module declaring it, + for the fifteen verbs of the Connectivity domain's seats that name no `callers` on main, which §68 would otherwise + close to their holders and the operator: `node-hostname`'s `entries`, `add` and `remove`; + `node-intrusion-prevention`'s `status`, `banned`, `ban` and `unban`; `node-packet-filter`'s `rules`, `reload` and + `remove`; `node-resolver`'s `routes`, `route` and `unroute`; `node-uplink`'s `resolvers` and `links`. Each of their + definitions in `mesh-connectivity-contracts` names `invokers` in this milestone. How it is checked: unit (definition reader), each form accepted and any other entry refused; the grants part's table test grants each form. - §337 **The bus module's restore points.** The verbs the holder of `node-backup` on the bus module's node calls to take and restore a module's kept data on the bus name `holder:node-backup@same-node`. Why: only that holder takes them @@ -257,7 +262,8 @@ record it follows from, and replaces the builder's own choice where one was made (assigner), a composition step's `run` is answered and its input read by digest. - §355 **The assigner says an unanswered `manifest-reads`.** The assigner, the one caller of the node-engine's `manifest-reads` from G, writes `call-unanswered` as its own finding and says it as an observer does (§383, §384), - so the condition keeper hears it with no change of its own (ADR 0668 §3; the operator, 2026-11-11). The assigner and + so the condition keeper hears it with no change of its own, departing from ADR 0668 §3's reads by the operator's + answer of 2026-11-11. The assigner and the runner each declare their own state `findings` in their old `state` section, both being foundation parts, read by their own read tools alone. How it is checked: whole (assigner, runner, condition keeper), a node-engine that never answers makes the assigner say `finding-written` for `call-unanswered` after five minutes, and the keeper @@ -297,12 +303,17 @@ record it follows from, and replaces the builder's own choice where one was made modules whose entry declares the finding events of §383, the node-engine among them, its holder read from the seat list. It joins the readers of *Every other reader of which modules exist*, reading every watched branch's newest catalog revision, so a module watched on any branch is a source. A condition of a module that is in no branch's - revision is cleared, said with the reason `module left the catalog`. Before its first view the keeper keeps the + revision is cleared, said with the reason `module left the catalog`. The keeper also takes a first view of the + assignment half (through `meshwantsclient`, following `assignment-revision` of `mesh-wants`), and a module's findings + of a node where the newest assignment graph revision no longer places that module are cleared, said with the reason + `module left the node`. Before its first view the keeper keeps the conditions it holds and applies no finding event, which waits on its durable consumer, and says `view-waits` after five minutes (*A part with no view says it waits*). It reads no module's `findings` state (§383). Why: no part fixes - a name (§7), and the keeper hears findings and reads no other module's state (ADR 0596 §2, §4; the operator, - 2026-11-11). How it is checked: whole (keeper), a fixture module declaring the finding events is heard with no code - change; the catalog's validation finds no `findings` in the keeper's manifest `reads`. + a name (§7), and the keeper hears findings and reads no other module's state (ADR 0596 §2, §4), departing from + ADR 0564 §2 by the operator's answer of 2026-11-11; the assignment half and `module left the node` are the design's + choice. How it is checked: whole (keeper), a fixture module declaring the finding events is heard with no code + change, and a fixture module's condition on a node is cleared with `module left the node` when an assignment graph + revision no longer places it there; the catalog's validation finds no `findings` in the keeper's manifest `reads`. ### From round 1 of the review by architectural domain (2026-11-10) @@ -505,11 +516,8 @@ events, and a module states its findings from its own truth; the shared tool-run (`headline`, `explanation`), `since` and `at`; `finding-cleared` carries `observer`, `node`, `scope`, `id`, `kind`, `at` and `why`. mesh-sdk's finding writer (`go/views`' `FindingWriter`) says them for every part, beside the key it writes in the part's own state `findings`, which stays the owner's and is read only by the owner's read tool. The - keeper hears them on one durable consumer of its own: its manifest's `consumes` names them with the emitter `*` - (`*.finding-written`, `*.finding-cleared`, `*.findings-stated`), a form that takes that event from every module - whose manifest declares it, and the grants part grants the subscription per declaring module, derived from the - manifests as every grant is (§59); the manifest validator takes `*` only as the whole emitter before a declared event - name. The keeper takes a finding only from a source its catalog view names (§365), raises or changes a condition on + keeper hears them on one durable consumer of its own, its manifest's `consumes` naming them with the emitter `*` + (`*.finding-written`, `*.finding-cleared`, `*.findings-stated`, §386). The keeper takes a finding only from a source its catalog view names (§365), raises or changes a condition on `finding-written` and clears it on `finding-cleared`, save while ADR 0564 §13 holds it; a condition's looks are those of its last event, and the observer's read tool shows its newest. What exists today goes in the same change: every `findings` entry of the keeper's manifest `reads`, its `consumes` of the assigner's and the indexer's @@ -517,10 +525,10 @@ events, and a module states its findings from its own truth; the shared tool-run indexer declare no `views.findings`, which no reader follows any more; the read tools stay for persons and sessions. No ordering edge is needed: a source still on its old version says nothing, its conditions stand as they are, and §384's bound names it. Why: only its owner reaches a module's state, and a part hears another's events (ADR 0596 §2, - §4); the operator, 2026-11-11. The subjects, the fields, the writer and the `*` emitter are the design's choice. + §4), departing from ADR 0564 §2 by the operator's answer of 2026-11-11. The subjects, the fields and the writer are + the design's choice. How it is checked: unit (mesh-sdk's writer), a finding written and one cleared each say their event with these - fields after the key; unit (manifest validator), `*.finding-written` accepted, `*.>` and `*` before an undeclared - event refused; whole (keeper), a fixture observer's `finding-written` raises a condition and its `finding-cleared` + fields after the key; the manifest validator's rows of §386; whole (keeper), a fixture observer's `finding-written` raises a condition and its `finding-cleared` clears it, and the same event from an account that is not the source's is refused by the bus; the catalog's validation finds no `findings` in the keeper's `reads`. - §384 **An observer states every finding it owns at its start and when the keeper asks; the keeper's view is @@ -544,7 +552,8 @@ events, and a module states its findings from its own truth; the shared tool-run with the full list states no `seat-list-unread`, and the keeper clears it at that start. Why: the node-engine's seat-list waiter kept only in its process that it had written its finding, so a node-engine restarted with the full list never cleared it, and two machines said they waited for the seat list for a day while both held it (issue 1966); - the operator, 2026-11-11: the fix is NATS-native, from each observer's own truth. The event, its fields, the bound, + departing from ADR 0564 §2 and §12 by the operator's answer of 2026-11-11: the fix is NATS-native, from each + observer's own truth. The event, its fields, the bound, the reasons and the replacement for an observer held once are the design's choice. How it is checked: whole (the node-engine, and each observer through mesh-sdk's writer), a module restarted after raising a finding whose cause has gone clears it at its start, the node-engine's `seat-list-unread` among them; whole (keeper), a `findings-asked` is @@ -556,3 +565,16 @@ events, and a module states its findings from its own truth; the shared tool-run granted, and call `merge-pull` or the runner's `run` as a session; G changes neither that account nor its grants, a known limit. Why: the operator, 2026-11-11; modules running as the machine's account are issue 1966's (note 193), whose fix carries its check. How it is checked: none in G; the limit is named on issue 1966. +- §386 **A `*` emitter in `consumes` hears one event from every module that declares it.** A `consumes` entry + `*.` takes the event `` from every module whose manifest declares it, and is the one generic form for + hearing an event that many modules say; the keeper's three finding events are the first (§383, §384). The manifest + validator takes `*` only as the whole emitter, and only before an event that a seat's definition or a contract + in mesh-sdk defines, with its fields (for the finding events, mesh-sdk's finding writer); it refuses `*` + before any other event, `*.>`, and `*` in part of an emitter's name. The grants part grants the subscription per + declaring module, the subject of each module whose manifest declares the event, derived from the manifests as every + grant is (§59), so a module that declares it later is heard with no change to the consumer's manifest, and a module + that does not declare it is not granted. Why: the keeper hears modules it finds in the catalog and its manifest names + none (§7, §365); the form, the validator's rule and the grant are the design's choice following the operator's + answer of 2026-11-11 (§383). How it is checked: unit (manifest validator), `*.finding-written` accepted, `*.>`, + `*x.finding-written` and `*` before an event no contract defines refused; the grants part's table test grants a + `*.finding-written` consumer the subject of each declaring module and of no other. -- 2.54.0 From 87827ebb82a2edf5b8ab4a6bd0ea77b5b0ff2523 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 02:30:59 +0100 Subject: [PATCH 13/13] Answer round 3 of the review of milestone G: a home and trust for the finding events, restarts that keep standing faults The finding events lacked a stream, a consumer per source, a rule against a restart clearing what still stands, a take-over rule and presence events; the 2026-11-11 answers move to a part file of their own so each stays under its size. The test budget is counted per module, the upgrade window between the builder and the registry holders gets its edge, and the readers of the new manifest words come first. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../47-delivery-from-commit-to-delivered.md | 16 +- .../G-1-pipelines-registration-and-steps.md | 8 +- .../G-2-pipelines-sizes-checks-and-tools.md | 9 +- .../01-to-be/47-milestones/G-7-answers.md | 136 ++++---------- .../G-8-findings-heard-as-events.md | 167 ++++++++++++++++++ 03-DESIGN/01-to-be/47-milestones/G.md | 5 +- .../51-the-registries-keep-what-is-named.md | 5 +- 7 files changed, 234 insertions(+), 112 deletions(-) create mode 100644 03-DESIGN/01-to-be/47-milestones/G-8-findings-heard-as-events.md 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 41f2695f..267d5a29 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 @@ -2,7 +2,7 @@ layer: to-be status: in-progress code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-catalog modules/gitea, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, mesh-contracts, hq 00-META/checks] -updated: 2026-11-10 +updated: 2026-11-11 decisions: - "ADR 0667" - "ADR 0666" @@ -1801,8 +1801,8 @@ integration test, which starts the module whole against the real bus and store; `modules/mesh-delivery-pipeline` in mesh-delivery, has one of its own. The checker's tests go with it at G's delivery, its whole-module test, its try test and the examiner's table among them, and so do the indexer's try-candidate rows and mesh-delivery's `tryevent`; tests 21 to 24, 26, 31, 41, 46, 52 and 58, which the checker proves, hold until then, and -the runner's tests below restate what of them stays (22 in 73, 23 in 84, 46 in 84, 58 in 84). No repository ends the -milestone with more tests than it starts with (research 085, Direction, One milestone; the test budget). +the runner's tests below restate what of them stays (22 in 73, 23 in 84, 46 in 84, 58 in 84). +No module ends the milestone with more tests than it starts with, counted per catalog module (research 085, Direction, One milestone; the test budget, per module, the operator 2026-11-11). 63. **R1, no hand act delivers a change.** A change to the indexer, the grants part and the tool runner is delivered end to end, and the hand-act log of the seat `mesh-hand-acts` names no act for it (live; mesh-delivery, mesh-tools). @@ -1871,7 +1871,9 @@ The failure paths: not run`, never passed; two modules owning one path are refused (whole, runner and assigner). 85. **A forged step result**, from an account other than the step's seat holder's, or naming another commit or fingerprint, is refused and said; a `step-ended` on another node's subject is refused by the bus; a verb whose - definition names no `callers` admits only the holder roles and the operator (whole, runner and grants part). + definition names no `callers` admits only the holder roles and the operator; a change adding a new `invokes` of + `node-packet-filter.remove`, a verb whose callers are `invokers`, names that grant in its `pipeline-ended`'s + `widens` (whole, runner and grants part). 86. **An author's own review** is refused; a reviewer's passes the step (whole, runner). 87. **A step seat with no holder** ends its pipeline failed, naming the seat, within five minutes (whole, runner). 88. **A runner stopped mid-step** takes its pipelines up again from its kept state when it starts, a replaced holder @@ -1958,6 +1960,12 @@ ADR 0667's checks, of milestone G itself: to be back" and nothing is raised; with a placing machine present that never runs it, `delivery-waits` is raised after 60 minutes naming it and its node-engine's version; each want names the released version and its parts' digests; `indexer_edges` answers the module's edges from both ends (whole, indexer, assigner and node-engine, mesh-delivery and `mesh-host`). +107. **A finding is stated from its observer's truth** (design 47 G §383, §384, §388). A fixture observer's + `finding-written` and `finding-cleared` raise and clear a condition; restarted after its cause has gone, it clears + its finding at its start; restarted with a call unanswered for two hours, it says neither `condition-cleared` nor + `condition-raised` (whole, condition keeper and a fixture observer, mesh-catalog and mesh-sdk). Live: after G's + delivery, a node-engine restarted with the full seat list clears its `seat-list-unread`, read with + `mesh-conditions.conditions`. ## What retires diff --git a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md index edbd1744..e6dcba40 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md +++ b/03-DESIGN/01-to-be/47-milestones/G-1-pipelines-registration-and-steps.md @@ -3,7 +3,7 @@ layer: to-be status: in-progress design: 47 milestone: G -updated: 2026-11-10 +updated: 2026-11-11 decisions: - "ADR 0668" - "ADR 0667" @@ -209,8 +209,10 @@ check and deliver milestone G itself (ADR 0667 §2); each of them says so in a l `mesh.seat..event.step-ended.`, the node that ran it in the subject and each holder's account granted only its own node's subject, through a durable consumer of its own, so a step outlives a restart of either side (ADR 0666 §7, ADR 0363 §2). A build seat's holder restarted mid-step gets the step again from its work queue and finishes it - (ADR 0664 §1). A unit or integration test step says its test counts in `step-ended`, and `pipeline-ended` names any - repository over 50 integration or 500 unit tests, and any catalog module over them (ADR 0666 §8; the test budget). A step's outcome is one of `passed`, `failed`, `nothing to run`, + (ADR 0664 §1). A unit or integration test step says its catalog module's test counts in `step-ended`; `pipeline-ended` carries each + catalog module's counts against the test budget, which is counted per catalog module (at most 50 integration and + 500 unit tests each), and `pipeline-ended` names any catalog module over it (ADR 0666 §8; the test budget, per + module, the operator 2026-11-11). A step's outcome is one of `passed`, `failed`, `nothing to run`, `bypassed`, `carried` (with the pipeline it was carried from), `could not run` (naming the part not heard), `waiting on `, `accepted` (a failed `optional` step a person accepted, with who and why), `not asked` (an `approval` step after a failed `required` step), `superseded` and `cancelled`. diff --git a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md index efb17334..ecd38119 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md +++ b/03-DESIGN/01-to-be/47-milestones/G-2-pipelines-sizes-checks-and-tools.md @@ -230,6 +230,9 @@ and an estimated 2 mesh-sdk publishes a day, each rebuilding about 120 artifacts | the assigner | `written`: the last source revision and end count per module and watched branch (§352) | old `state` section | until the branch is unwatched | a few KiB | | the indexer and the assigner | `inputs`: a `run` ask's input by write time and sha256 (§354) | old `state` section | until the ask's `step-ended`, at most a day | 64 MiB; an estimated 300 asks a day of under 100 KiB each is under 30 MiB; at the cap the asker refuses the ask loudly, naming the store | | the runner and the assigner | `findings` (§355), read by their own read tools alone; the keeper hears the findings as events (§383) | old `state` section | an entry deleted when its finding clears | a few KiB | +| every observer (§387) | the stream `-findings`: `finding-written`, `finding-cleared`, `findings-stated` (§383) | old `streams` section for a foundation part, the module's `streams` otherwise | 7 days, its oldest dropped when full; the next statement covers a lost one (§384) | 16 MiB; an estimate of a few hundred findings a day of about 1 KiB is under 2 MiB a week | +| the condition keeper | `findings-views`: one key per observer and node, its last stated set, pending keys and open asks (§388) | the form of its `conditions` | deleted when the module leaves the catalog or the node (§365) | under 1 MiB; about 30 observers on four machines | +| the watchman, the keeper | `machine-away`, `machine-back` (§389); `findings-asked` (§390) | the seats' definitions | 90 days; 7 days | 16 MiB each | | the runner | `aged`: three step-log sequences (§330) | old `state` section | never deleted | bytes | | the teller | `widens-said` (§335) | the template `state` | until the branch is unwatched | a few KiB; the template's | | each build module | `steps` (§326) | the template `state` | one key per artifact, dropped by `steps` with `drop` | under 1 MiB; the template's | @@ -269,7 +272,11 @@ step keys and the builder's build log, against the checker's last week (test 99) from the next change on; mesh-delivery's `contracts` and `sdk` move under its `modules/` folder (ADR 0667 §3). - §89 **Readers first** (ADR 0667 §4): an edge of kind 4 each, which the delivery plan derives: the indexer's manifest validator for the seat definitions' new fields (`callers`, `acts`, `subject`, `keep`) before any definition using - them; on each node the node-engine that reads `form` and `catch-up` before any manifest writing them. + them; the manifest validator for the manifests' new words (`version`, §368; a `*` emitter in `consumes`, §386; the + finding stream an observer declares, §383, §387) and the grants part that makes a `*` entry's durable consumers + (§386), before the keeper's or any observer's manifest using them; on each node the node-engine that reads `form` + and `catch-up` before any manifest writing them. The running checker's validator makes G's own check, so the reading + of these words goes in the reader release of §340, whether or not that validator already takes one of them. - §90 **The stream maker first** (ADR 0667 §5): the grants part, which makes each kept seat stream and its durable consumers, before the parts that hear a kept stream. - §91 **The bus module first** (ADR 0667 §6): the bus module, with `-objects`, the sent store and the grant of each diff --git a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md index ba512476..0714bc9f 100644 --- a/03-DESIGN/01-to-be/47-milestones/G-7-answers.md +++ b/03-DESIGN/01-to-be/47-milestones/G-7-answers.md @@ -125,9 +125,8 @@ record it follows from, and replaces the builder's own choice where one was made reads that no module name stands in the runner's code (§7). - §334 **A publication's pipelines and their key.** `package-published` carries the package's artifact reference (`:`, from the catalog entry), its version and digest, the watched branch and the source revision - its build was made from. The runner reads the dependents from that revision's catalog graph, the artifacts with a - kind-1 edge to the package, and starts each one's pipeline keyed by that branch, that source revision and the - dependent module, the version in the pipeline's state; a `take-in.taken` of the same revision and module is the + its build was made from. The runner asks `outdated` for that package on that branch and starts a pipeline for each + dependent `outdated` names behind (§53), keyed by that branch, that source revision and the dependent module, the version in the pipeline's state; a `take-in.taken` of the same revision and module is the same pipeline (*A second start writes nothing*). A pipeline the runner starts from `outdated` (its start, §375, or `runner_catch_up`) is keyed by the dependency's watched branch, the dependency's source revision `outdated` names, and the dependent module, so it is the same pipeline the event would have started. Why: a pipeline's key is branch, source revision and module (above), @@ -157,7 +156,8 @@ record it follows from, and replaces the builder's own choice where one was made close to their holders and the operator: `node-hostname`'s `entries`, `add` and `remove`; `node-intrusion-prevention`'s `status`, `banned`, `ban` and `unban`; `node-packet-filter`'s `rules`, `reload` and `remove`; `node-resolver`'s `routes`, `route` and `unroute`; `node-uplink`'s `resolvers` and `links`. Each of their - definitions in `mesh-connectivity-contracts` names `invokers` in this milestone. How it is checked: unit (definition reader), each + definitions in `mesh-connectivity-contracts` names `invokers`, `persons` and `sessions` in this milestone, since + sessions and persons call them today through the mesh MCP server. How it is checked: unit (definition reader), each form accepted and any other entry refused; the grants part's table test grants each form. - §337 **The bus module's restore points.** The verbs the holder of `node-backup` on the bus module's node calls to take and restore a module's kept data on the bus name `holder:node-backup@same-node`. Why: only that holder takes them @@ -175,7 +175,9 @@ record it follows from, and replaces the builder's own choice where one was made above. Why: one source of caller limits (ADR 0666 §20). How it is checked: the generator's test refuses an unknown entry. - §340 **The reader release first.** mesh-sdk's seat-definition reader released before G's check reads `callers` as text - or as a list; G's own reader refuses text. Why: readers first (ADR 0667 §4), and every definition is a list from G; it is merged ahead of G's check because the + or as a list; G's own reader refuses text. The same release carries mesh-sdk's manifest reader of the new words of + §89 (`version`, a `*` emitter in `consumes`, an observer's finding stream), which the running checker's validator + reads before G's manifests use them. Why: readers first (ADR 0667 §4), and every definition is a list from G; it is merged ahead of G's check because the running checker, which makes G's own check (ADR 0667 §2), must read G's definitions, the operator's choice of a reader first (2026-11-09). How it is checked: unit rows for both forms in the release, and for the refusal in G. @@ -303,17 +305,21 @@ record it follows from, and replaces the builder's own choice where one was made modules whose entry declares the finding events of §383, the node-engine among them, its holder read from the seat list. It joins the readers of *Every other reader of which modules exist*, reading every watched branch's newest catalog revision, so a module watched on any branch is a source. A condition of a module that is in no branch's - revision is cleared, said with the reason `module left the catalog`. The keeper also takes a first view of the - assignment half (through `meshwantsclient`, following `assignment-revision` of `mesh-wants`), and a module's findings - of a node where the newest assignment graph revision no longer places that module are cleared, said with the reason - `module left the node`. Before its first view the keeper keeps the - conditions it holds and applies no finding event, which waits on its durable consumer, and says `view-waits` after - five minutes (*A part with no view says it waits*). It reads no module's `findings` state (§383). Why: no part fixes + revision is cleared, said with the reason `module left the catalog`. A module's findings of a node are cleared, + said with the reason `module left the node`, when the node-engine seat's `running..` says the module no + longer runs there and the newest assignment graph revision, from the keeper's first view of the assignment half + (through `meshwantsclient`, following `assignment-revision` of `mesh-wants`), no longer places it there; never for a + module no revision placed on that node. For it, `running..` (§309) carries `stopped`, with no + fingerprint, once the node-engine's have no longer holds the module, on the same subject, so the stream kept last per + subject keeps it. Until its first view the keeper applies the events of the sources it keeps (§388) and says + `view-waits` after five minutes (*A part with no view says it waits*), and a missing assignment half holds only the + clears of `module left the node`. It reads no module's `findings` state (§383). Why: no part fixes a name (§7), and the keeper hears findings and reads no other module's state (ADR 0596 §2, §4), departing from ADR 0564 §2 by the operator's answer of 2026-11-11; the assignment half and `module left the node` are the design's - choice. How it is checked: whole (keeper), a fixture module declaring the finding events is heard with no code - change, and a fixture module's condition on a node is cleared with `module left the node` when an assignment graph - revision no longer places it there; the catalog's validation finds no `findings` in the keeper's manifest `reads`. + choice, and so is `stopped`. How it is checked: whole (keeper, node-engine), a fixture module declaring the finding + events is heard with no code change; a fixture module's condition on a node is cleared with `module left the node` + once the node-engine says `stopped` for it and the assignment graph no longer places it, and is not cleared on the + assignment revision alone; the catalog's validation finds no `findings` in the keeper's manifest `reads`. ### From round 1 of the review by architectural domain (2026-11-10) @@ -361,7 +367,8 @@ record it follows from, and replaces the builder's own choice where one was made (`go/vMAJOR.MINOR.PATCH`, design 51) is written by the builder only on the catalog's `version-released`, through the seat `git`'s verb `tag`, whose callers are the builder alone, so a superseded build's reserved version is never tagged. How it is checked: whole (Go and TypeScript build modules), a built package and program carry the reserved - version; whole (builder), the tag is written on `version-released` and never for a superseded build. + version; whole (builder), the tag is written on `version-released` and never for a superseded build; whole (forge + module), a `tag` from any caller but the builder's account is refused by the bus. - §371 **Only the first watched branch releases.** Only a build of the registration's first watched branch reserves and releases a version; a build of another watched branch reserves none, publishes no version and is delivered by its build, its fingerprint, as every want names one. Why: the operator, 2026-11-10. How it is checked: whole (builder), @@ -369,10 +376,14 @@ record it follows from, and replaces the builder's own choice where one was made - §372 **The catalog's versions start from what is published.** At the new indexer's first start, `versions` is filled once, as released, for each module from the highest `published` version over every artifact of the module that the builder's state `versions` held (for mesh-sdk the higher of Go's `v0.1.14` and npm's `0.1.18`, so `0.1.18`), read by - `builder_versions` as a kept call (ADR 0668 §1, §2), an unanswered call raising `call-unanswered` after five minutes; + `builder_versions` as a kept call (ADR 0668 §1, §2), which the indexer's manifest names in its `invokes` + (`mesh-delivery-builder.builder_versions`) until that tool goes, an unanswered call raising `call-unanswered` after five minutes; it is not filled again. Then the indexer says `versions-filled` on its seat, once, kept on its stream with no age limit, and the builder reserves nothing before it (§369). The builder keeps serving `builder_versions` until then: the - running builder serves it already, and the new one serves it too, so no ordering edge is needed. The builder's state + running builder serves it already, and the new one serves it too, so no ordering edge is needed. The new builder's + `builder_versions` also answers each reserved version with its artifacts' digests (§369), so a registry holder still + on its version before G, which asks `builder_versions`, stores a publish of a reserved version made between the + builder's delivery and its own (§381). The builder's state `versions`, the floor and its tool `builder_floor_again`, the event `numbered`, `builder_versions`, and `tag-asks` are removed in the first change after the first pipeline after G's delivery has delivered a change end to end, the state through `drop` on a person's word (ADR 0595 §4). Why: numbering reads only the catalog's released versions (§348), @@ -444,7 +455,7 @@ record it follows from, and replaces the builder's own choice where one was made again. A dependent's new version that a change orders after its dependency's new version by kind 2, 4 or 6 is wanted only once that dependency's version is delivered to every machine that places it and is present. The assigner judges it, since placement is its own state: from its placements, the node-engine seat's `running` events and the - watchman's presence of each node, judged again on each of them and on each placement change, and says + watchman's `machine-away` and `machine-back` of each node (§389), never its state, judged again on each of them and on each placement change, and says `version-delivered` (the module, the version, its parts' digests and the machines away) on its seat `mesh-wants` (`mesh.seat.mesh-wants.event.version-delivered`) once every present machine placing it runs it healthy; the catalog hears it for its entries (§374), and the assigner writes the dependent's wants on it. The dependent's wait is the @@ -492,89 +503,14 @@ record it follows from, and replaces the builder's own choice where one was made seat asks the seat `mesh-builds`' verb `reserved` (§369), in place of `builder_versions`, whether the module, the version and the artifact's digest are reserved, and refuses loudly otherwise (ADR 0605 §4); a version the catalog already holds released for that digest is answered as held and stores nothing. Why: ADR 0605 §4 has the holder - refuse a version the builder did not give, and the builder's list goes (§372); the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: whole - (registry holder, builder), a publish of an unreserved version is refused, naming it. + refuse a version the builder did not give, and the builder's list goes (§372); the design's choice, round 2 of the review by architectural domain, 2026-11-10. + The order is a served-verb edge (kind 6) the delivery plan derives from the holder's new `invokes` of + `mesh-builds.reserved`: the builder runs its new version, which serves `reserved`, before each registry holder's new + version is delivered; until then the old holder asks `builder_versions`, which answers reserved versions (§372). How + it is checked: whole (registry holder, builder), a publish of an unreserved version is refused, naming it; whole + (builder, registry holder on its version before G), a publish made between the builder's delivery and the holder's + is stored; the delivery plan of G names the edge from the builder to each registry holder. - §382 **An older version released after a newer starts nothing.** When every artifact of a version lower than the module's highest released is published late (a retry of an older reservation), the catalog records it `released late`, says no `version-released` for it, and the runner starts no rebuild on its `package-published` (§53). Why: a rebuild against an older part would move a dependent back; the design's choice, round 2 of the review by architectural domain, 2026-11-10. How it is checked: test 102. - -### The operator's answers to milestone G's design questions (2026-11-11) - -*ADR 0596 §1, §2, §4, ADR 0564 §2, §12, §13, ADR 0668 §1, §3, ADR 0636 §1, ADR 0666 §16, ADR 0363 §2. The operator, -2026-11-11, through lead-47: the condition keeper reads no other module's state and hears each module's findings as -events, and a module states its findings from its own truth; the shared tool-runner account stays as it is in G.* - -- §383 **An observer says its findings as events; the keeper reads no observer's state.** Every module that writes - findings (those the keeper reads today and those G adds: the watchman, the self-check, the healers, the runner, the - assigner, the indexer, the builder, the forge bridge and the node-engine among them) is an observer, and says each - change of a finding as an event of its own, declared in its manifest's `streams` and published by its account alone - on `mesh.stream..event..`, the node it runs on in the subject (§260, ADR 0363 §2): - `finding-written` when a finding is raised or what it says changes (only then, ADR 0289 §1, §4), and - `finding-cleared` when it no longer observes it. `finding-written` carries `observer` (the module), `node`, `scope`, - `id` and `kind` (the key `..` of ADR 0564 §2), `severity`, `subject`, `evidence`, ADR 0253's words - (`headline`, `explanation`), `since` and `at`; `finding-cleared` carries `observer`, `node`, `scope`, `id`, `kind`, - `at` and `why`. mesh-sdk's finding writer (`go/views`' `FindingWriter`) says them for every part, beside the key it - writes in the part's own state `findings`, which stays the owner's and is read only by the owner's read tool. The - keeper hears them on one durable consumer of its own, its manifest's `consumes` naming them with the emitter `*` - (`*.finding-written`, `*.finding-cleared`, `*.findings-stated`, §386). The keeper takes a finding only from a source its catalog view names (§365), raises or changes a condition on - `finding-written` and clears it on `finding-cleared`, save while ADR 0564 §13 holds it; a condition's looks are - those of its last event, and the observer's read tool shows its newest. What exists today goes in the same change: - every `findings` entry of the keeper's manifest `reads`, its `consumes` of the assigner's and the indexer's - `views.findings` and its `invokes` of `assigner_read_findings` and `indexer_read_findings`; the assigner and the - indexer declare no `views.findings`, which no reader follows any more; the read tools stay for persons and sessions. - No ordering edge is needed: a source still on its old version says nothing, its conditions stand as they are, and - §384's bound names it. Why: only its owner reaches a module's state, and a part hears another's events (ADR 0596 §2, - §4), departing from ADR 0564 §2 by the operator's answer of 2026-11-11. The subjects, the fields and the writer are - the design's choice. - How it is checked: unit (mesh-sdk's writer), a finding written and one cleared each say their event with these - fields after the key; the manifest validator's rows of §386; whole (keeper), a fixture observer's `finding-written` raises a condition and its `finding-cleared` - clears it, and the same event from an account that is not the source's is refused by the bus; the catalog's - validation finds no `findings` in the keeper's `reads`. -- §384 **An observer states every finding it owns at its start and when the keeper asks; the keeper's view is - rebuilt from those events alone.** At each start, and on each ask of the keeper, an observer judges every finding it - owns anew from its own truth, the cause each kind watches (the seat list it holds, a call still unanswered, a wait - still standing), never from what a process remembers and never from its `findings` state, and says `findings-stated` - on `mesh.stream..event.findings-stated.`, carrying `observer`, `node`, `at`, `asked` (the ask it - answers, none at a start) and `findings`, every finding it observes now with `finding-written`'s fields; a finding - whose bound has not passed since the start (a wait of under five minutes) is not among them. The keeper sets its view - of that observer on that node to exactly that set: it raises what is new and clears each condition of that observer - and node the set does not name, with the reason `not stated at 's start` or `not stated when asked`; for an - observer whose catalog entry claims only seats held once for the mesh, the set replaces its findings of every node, - so a holder moved to another machine leaves none behind. The keeper asks with its seat's event `findings-asked` - (`mesh.seat.mesh-conditions.event.findings-asked`, carrying `ask`, `at`, `by` and, to ask one, `module`), declared in - the seat's definition and heard by every observer through mesh-sdk's writer, which declares it in the observer's - `consumes`. It asks after its first view (§365) at each of its starts, and on `resync` (ADR 0564 §12), which so reads - no state. An observer that states nothing within five minutes of an ask while its machine is present is the keeper's - own finding `findings-unstated`, naming the module and the node, cleared by its next `findings-stated`; its conditions - stand meanwhile, and an observer on a machine away is not named. A set larger than one bus message goes in the - observer's sent store (§59). The node-engine's `seat-list-unread` (ADR 0636 §1) is stated so: a node-engine started - with the full list states no `seat-list-unread`, and the keeper clears it at that start. Why: the node-engine's - seat-list waiter kept only in its process that it had written its finding, so a node-engine restarted with the full - list never cleared it, and two machines said they waited for the seat list for a day while both held it (issue 1966); - departing from ADR 0564 §2 and §12 by the operator's answer of 2026-11-11: the fix is NATS-native, from each - observer's own truth. The event, its fields, the bound, - the reasons and the replacement for an observer held once are the design's choice. How it is checked: whole (the - node-engine, and each observer through mesh-sdk's writer), a module restarted after raising a finding whose cause has - gone clears it at its start, the node-engine's `seat-list-unread` among them; whole (keeper), a `findings-asked` is - answered by a fixture observer's `findings-stated`, a condition the set does not name is cleared with its reason, and - an observer that states nothing raises `findings-unstated` after five minutes; unit (mesh-sdk's writer), a start - states only the findings observed now. -- §385 **One tool-runner account for every module of a machine stays as it is.** On a machine where one tool runner - serves every module's tools on one bus account, any tools bundle there may read any sent store (§59) that account is - granted, and call `merge-pull` or the runner's `run` as a session; G changes neither that account nor its grants, a - known limit. Why: the operator, 2026-11-11; modules running as the machine's account are issue 1966's (note 193), - whose fix carries its check. How it is checked: none in G; the limit is named on issue 1966. -- §386 **A `*` emitter in `consumes` hears one event from every module that declares it.** A `consumes` entry - `*.` takes the event `` from every module whose manifest declares it, and is the one generic form for - hearing an event that many modules say; the keeper's three finding events are the first (§383, §384). The manifest - validator takes `*` only as the whole emitter, and only before an event that a seat's definition or a contract - in mesh-sdk defines, with its fields (for the finding events, mesh-sdk's finding writer); it refuses `*` - before any other event, `*.>`, and `*` in part of an emitter's name. The grants part grants the subscription per - declaring module, the subject of each module whose manifest declares the event, derived from the manifests as every - grant is (§59), so a module that declares it later is heard with no change to the consumer's manifest, and a module - that does not declare it is not granted. Why: the keeper hears modules it finds in the catalog and its manifest names - none (§7, §365); the form, the validator's rule and the grant are the design's choice following the operator's - answer of 2026-11-11 (§383). How it is checked: unit (manifest validator), `*.finding-written` accepted, `*.>`, - `*x.finding-written` and `*` before an event no contract defines refused; the grants part's table test grants a - `*.finding-written` consumer the subject of each declaring module and of no other. diff --git a/03-DESIGN/01-to-be/47-milestones/G-8-findings-heard-as-events.md b/03-DESIGN/01-to-be/47-milestones/G-8-findings-heard-as-events.md new file mode 100644 index 00000000..3daebf3a --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-8-findings-heard-as-events.md @@ -0,0 +1,167 @@ +--- +layer: to-be +status: in-progress +design: 47 +milestone: G +updated: 2026-11-11 +decisions: + - "ADR 0668" + - "ADR 0667" + - "ADR 0666" + - "ADR 0636" + - "ADR 0603" + - "ADR 0596" + - "ADR 0595" + - "ADR 0564" + - "ADR 0363" + - "ADR 0289" + - "ADR 0253" +--- + +# 47, milestone G: the operator's answers of 2026-11-11, findings heard as events + +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". The lines +§336, §355 and §365, which these answers also change, stay in [G-7-answers](G-7-answers.md). + +## The operator's answers to milestone G's design questions (2026-11-11) + +*ADR 0596 §1, §2, §4, ADR 0564 §2, §4, §12, §13, ADR 0668 §1, §3, ADR 0636 §1, ADR 0666 §16, ADR 0667 §4, ADR 0363 +§1, §2, §5. The operator, 2026-11-11, through lead-47: the condition keeper reads no other module's state and hears +each module's findings as events; a module states its findings from its own truth, NATS-native; the shared +tool-runner account stays as it is in G; no record is written for these answers, so each line that departs from a +record says so in its Why. Round 3 of the review by architectural domain (2026-11-11) completed these lines.* + +- §383 **An observer says its findings as events on a stream of its own; the keeper reads no observer's state.** Every + module that writes findings is an observer (§387 names them) and says each change of a finding on its own stream + `-findings` (ADR 0363 §1: under its name in capitals on the bus, its `events` `finding-written.*`, + `finding-cleared.*` and `findings-stated.*`, kept 7 days and 16 MiB, its oldest message dropped when full, since the + next statement of §384 covers a lost one), published on `mesh.stream..event..`, the node it runs + on in the subject (ADR 0363 §2; §260). `finding-written` is said when a finding is raised or what it says changes, + and only then (ADR 0289 §1, §4); `finding-cleared` when the observer no longer observes it. `finding-written` + carries `scope`, `id` and `kind` (the key `..` of ADR 0564 §2), `severity`, `subject`, `evidence`, + ADR 0253's words (`headline`, `explanation`), `since` and `at`; `finding-cleared` carries `scope`, `id`, `kind`, `at` + and `why`. The observer and the node are the subject's alone: the grants part grants each module's account publish + on its own stream's subjects of its own node's segment only, the keeper takes both from the subject, and it refuses + an event whose body names another observer or node, said in its journal. mesh-sdk's finding writer (`go/views`' + `FindingWriter`) says them for every part, after the key it writes in the part's own state `findings`, which stays + the owner's and is read only by the owner's read tool; a publish the bus does not acknowledge is sent again for one + minute, then written in the part's journal as an error naming the finding, and the next statement covers it. The + keeper takes a finding only from a source of its own list (§388) and raises or changes a condition on + `finding-written` and clears it on `finding-cleared`, save while ADR 0564 §13 holds it; a condition's looks are those + of its last event, and the observer's read tool shows its newest. What exists today goes in the same change: every + `findings` entry of the keeper's manifest `reads`, its `consumes` of the assigner's and the indexer's + `views.findings`, and its `invokes` of `assigner_read_findings` and `indexer_read_findings`; the assigner and the + indexer no longer declare `views.findings`, which no reader follows; the read tools stay for persons and sessions. + Why: only its owner reaches a module's state, and a part hears another's events (ADR 0596 §2, §4); this departs from + ADR 0564 §2, where the keeper watches each observer's bucket, by the operator's answer of 2026-11-11. The stream, its + age and cap, the subjects, the fields, the writer and its retry are the design's choice. How it is checked: unit + (mesh-sdk's writer), a finding written and one cleared each say their event with these fields after the key, and an + unacknowledged publish is sent again and then logged; whole (keeper, a fixture observer on an account of its own), + `finding-written` raises a condition and `finding-cleared` clears it, the same event published by another module's + account and by the same module's account on another node's subject are refused by the bus, and a body naming another + observer or node is refused by the keeper; the catalog's validation finds no `findings` in the keeper's `reads`. +- §384 **An observer states every finding it owns at its start and when the keeper asks; the keeper's view is + rebuilt from those events alone.** At each start, and on each ask of the keeper, an observer judges every finding it + owns anew from its own truth, the cause each kind watches (the seat list it holds, a call still unanswered, a wait + still standing), never from what one process remembers, and says `findings-stated` on + `mesh.stream..event.findings-stated.`, carrying `at`, `asked` (the ask it answers, none at a start), + `findings` (every finding it observes now, with `finding-written`'s fields) and `pending`. A wait's bound counts from + the wait's own start, which the observer keeps in its own state when the wait begins (the `since` of its key in + `findings`, written before the bound passes), never from the process's start, so a wait that began before a restart + is judged against its whole age. `pending` names, with their `since` and their bound, the keys the observer cannot + judge yet since its start (a kept call it has not yet made again, a wait it has not yet looked at). The keeper sets + its view of that observer on that node to that set: it raises what is new, keeps each pending key's condition open + and unchanged, saying nothing for it, and clears each other condition of that observer and node the set does not + name, with the reason `not stated at 's start` or `not stated when asked`; a pending key is cleared only if + neither a `finding-written` nor a later set names it within its bound after the set's `at`, with the reason `not + judged within its bound`. For an observer whose catalog entry claims only seats held once for the mesh, the set + replaces its findings of every node, and the keeper takes `finding-written` and `finding-cleared` of it only from + the node the seat list names as its holder, and only with an `at` later than that holder's last `findings-stated`, so + a holder that took over leaves no finding of the one before and an old holder's late event changes nothing. A set + larger than one bus message goes in the observer's sent store (§59). The node-engine's `seat-list-unread` (ADR 0636 + §1) is stated so: a node-engine started with the full list states no `seat-list-unread`, and the keeper clears it at + that start. Why: the node-engine's seat-list waiter kept only in its process that it had written its finding, so a + node-engine restarted with the full list never cleared it, and two machines said they waited for the seat list for a + day while both held it (issue 1966); this departs from ADR 0564 §2 by the operator's answer of 2026-11-11: the fix is + NATS-native, from each observer's own truth. The event, its fields, `pending`, the kept start, the reasons and the + rule for an observer held once are the design's choice. How it is checked: whole (the node-engine, and each observer + through mesh-sdk's writer), a module restarted after raising a finding whose cause has gone clears it at its start, + the node-engine's `seat-list-unread` among them, and an observer restarted with a call unanswered for two hours says + neither `condition-cleared` nor `condition-raised`; whole (keeper), a condition the set does not name is cleared with + its reason, a pending key past its bound is cleared, and in a take-over the new holder's set clears the old holder's + findings and an old holder's later `finding-written` raises nothing; unit (mesh-sdk's writer), a start states the + findings observed now and pends the rest. Test 107. +- §385 **One tool-runner account for every module of a machine stays as it is.** On a machine where one tool runner + serves every module's tools on one bus account, any tools bundle there may read any sent store (§59) that account is + granted, call `merge-pull` or the runner's `run` as a session, and say the finding events of §383 of any module that + account publishes for; G changes neither that account nor its grants, a known limit. Why: the operator, 2026-11-11; + modules running as the machine's account are issue 1966's (note 193), whose fix carries its check. How it is checked: + none in G; the limit is named on issue 1966. +- §386 **A `*` emitter in `consumes` hears one event from every module that declares it.** A `consumes` entry + `*.` takes the event `` from every module whose manifest declares it, and is the one generic form for + hearing an event that many modules say; the keeper's `*.finding-written`, `*.finding-cleared` and + `*.findings-stated` are the first. The manifest validator takes `*` only as the whole emitter, and only before an + event that a seat's definition or a contract in mesh-sdk defines, with its fields (for the finding events, mesh-sdk's + finding writer); it refuses `*` before any other event, `*.>`, and `*` in part of an emitter's name. The grants part, + from the manifests as it derives every grant (§59), makes on each declaring module's stream one durable consumer + for the consumer, named `_` as ADR 0363 §5 names one, with its grants to read and acknowledge it and + its membership's word to bind it; it drops that consumer when the module no longer declares the event, and makes one + for a module that declares it later, with no change to the consumer's manifest. A `*` entry never reaches a + restricted stream (one with a `grant`, ADR 0363 §4). Why: the keeper hears modules it finds in the catalog and its + manifest names none (§7, §365); this departs from ADR 0363 §5, where an entry with a wildcard emitter never reaches + a module's stream, by the operator's answer of 2026-11-11; the validator's rule and the per-module consumer are the + design's choice. How it is checked: unit (manifest validator), `*.finding-written` accepted, `*.>`, + `*x.finding-written` and `*` before an event no contract defines refused; the grants part's table test makes a + `*.finding-written` consumer one durable consumer on each declaring module's stream and none on a restricted + stream; whole (keeper, grants part), a fixture module that declares the events after the keeper started is heard, + and a keeper stopped while findings change catches up from its durable consumers at its start. +- §387 **G's change writes the finding stream into every observer's manifest.** In the same change G writes the stream + `-findings` of §383 into the manifest's `streams`, and `mesh-conditions.findings-asked` into its `consumes`, + for every module that declares a state `findings` on main: `artifact-store-tools`, `claude-code`, `distribution`, `gitea`, + `mesh-build-go`, `mesh-config`, `mesh-healers`, `mesh-knowledge`, `mesh-provisioner`, `mesh-self-check`, `mesh-vault`, `mesh-watcher`, + `minio` and `nfs-server` in mesh-catalog; `mesh-delivery-assigner`, `mesh-delivery-builder`, `mesh-delivery-checker` (until its + removal, ADR 0667 §12), `mesh-delivery-indexer`, `mesh-delivery-teller`, `mesh-delivery-view`, `mesh-delivery-watchman` and + `mesh-enrolment` in mesh-delivery; `mesh-host`, the node-engine; `node-tools`, the tool runner, in mesh-tools; and the modules that gain + findings in G, the runner (`mesh-delivery-pipeline`, §355) and the forge bridge (`mesh-delivery-forge-bridge`, for `branch-unprotected`, §17). The manifest + validator refuses a manifest declaring the state `findings` without that stream and that `consumes`. Why: an + observer whose stream is missing says nothing the keeper hears; the list is read from main on 2026-11-11 and the rule + is the design's choice. How it is checked: unit (manifest validator), a manifest with `findings` and no finding + stream refused; the catalog's validation at G's heads refuses none of these manifests. +- §388 **The keeper keeps its sources in its own state, and raises its own findings itself.** The keeper keeps its + list of sources (each module whose catalog entry declares the finding stream, with the nodes it heard and each + observer's last stated set and open asks) in its own state `findings-views`, in the form of its `conditions`, one key + per observer and node, deleted when the module leaves the catalog (§365) or the node (§365). At its start it applies + the events of those sources at once from its durable consumers, before its first view of the catalog half; the view + only adds and removes sources. A source new to the list is asked with `findings-asked` naming it once its durable + consumer exists (§386). The keeper's own findings (`view-waits`, `findings-unstated`, `call-unanswered`) it raises and + clears directly as the one writer of conditions, never through its own events. A missing assignment half holds only + the clears of `module left the node`. Why: a keeper that waits for its view would hear nothing after a restart while + the catalog is down; the state and its key are the design's choice. How it is checked: whole (keeper), started with + the catalog's holder stopped it raises and clears from its kept sources' events, raises `view-waits` after five + minutes itself, and clears nothing with `module left the node` until the assignment half answers. +- §389 **The watchman says a machine's presence as events.** The seat `mesh-watchdogs` gains the events `machine-away` + and `machine-back` (`mesh.seat.mesh-watchdogs.event.machine-away.`, `.machine-back.`, carrying the node, + `at` and since when it was last heard), said by the watchman when its presence key of a machine expires and when the + machine is heard again (ADR 0564 §4), declared in the seat's definition and kept 90 days, 16 MiB. The keeper + consumes them for `findings-unstated` (no machine away is named, and an ask is said again to a module of a machine + back), and the assigner for its judgement of `version-delivered` (§376); neither reads the watchman's state. Why: + ADR 0596 §2; the events and their fields are the design's choice. How it is checked: whole (watchman), a machine + whose presence expires says `machine-away` and one heard again `machine-back`; whole (keeper), an observer on a + machine away is not named in `findings-unstated`. +- §390 **The keeper's ask, its tools, and its hold.** The keeper asks with its seat's event `findings-asked` + (`mesh.seat.mesh-conditions.event.findings-asked`, carrying `ask`, `at`, `by` and, to ask one, `module`), declared + in the seat's definition, kept 7 days and 16 MiB; an observer hears it through mesh-sdk's writer on a consumer that + starts from the newest ask, so an old ask is never answered again. The keeper asks after its start (§388) and on + `resync` (ADR 0564 §12), which gains an optional `module` and so reads no state; its callers are the operator, the + healers and agent sessions, and each call is written in the hand-act log of the seat `mesh-hand-acts`. An observer + that states nothing within five minutes of an ask, its machine not away (§389), is the keeper's finding + `findings-unstated`, naming the module and the node, cleared by its next `findings-stated`; its conditions stand + meanwhile. `findings-unstated` is held under ADR 0564 §13's hold while its module is being delivered, and G's + delivery plan says it may show while the observers move. The keeper's tool `conditions_findings_views` answers each + observer and node of `findings-views`: its last stated set with its `at`, its pending keys and the open asks; its + callers are any person, agent sessions and the healers. Why: ADR 0564 §12 has every act of the keeper started by + hand, and the tools-first rule a read of all it keeps; the names, the callers and the bound are the design's choice. + How it is checked: whole (keeper), `findings-unstated` is cleared by a `resync` naming that one module, the call is + in the hand-act log, an observer restarted after an old ask answers only a new one, and the tool answers a fixture + observer's set. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index eb95d6e1..de83d5c5 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -75,7 +75,7 @@ file on 2026-11-10, and is cited as "design 47 G §n". A sentence that holds a d stayed in the main file, and G builds what it says from G's delivery. A section named in italics is in this part, or, when it is not here, in the main file. -The part is seven files, each read whole, its design lines numbered across them: +The part is eight files, each read whole, its design lines numbered across them: - [G-1-pipelines-registration-and-steps](G-1-pipelines-registration-and-steps.md): pipelines per module: registration, watched branches, steps and policy, what holds a delivery, approval, groups, dependencies, a publish. - [G-2-pipelines-sizes-checks-and-tools](G-2-pipelines-sizes-checks-and-tools.md): pipelines per module: any size, the pipeline makes the check, who may say what, health, recovery, tools, kept data, how G itself is delivered, the choices under ADR 0666 and ADR 0667. @@ -83,4 +83,5 @@ The part is seven files, each read whole, its design lines numbered across them: - [G-4-revision-view-and-steps](G-4-revision-view-and-steps.md): the revision view, what ADR 0656 leaves to this design, the choices under ADR 0656, what each step says and their tools. - [G-5-acting-parts-check-and-kept-data](G-5-acting-parts-check-and-kept-data.md): the acting parts, the removed hold, the check on the change's own acting parts, after the merge, taken in, and kept data on the bus. - [G-6-verbs-and-retires](G-6-verbs-and-retires.md): the verbs G adds and what retires. -- [G-7-answers](G-7-answers.md): the answers to the builders', the reviewers' and the operator's questions (§322 on). +- [G-7-answers](G-7-answers.md): the answers to the builders', the reviewers' and the operator's questions (§322 to §382). +- [G-8-findings-heard-as-events](G-8-findings-heard-as-events.md): the operator's answers of 2026-11-11, findings heard as events (§383 on). diff --git a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md index 44f758a2..0daf1a1c 100644 --- a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md +++ b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md @@ -11,7 +11,7 @@ code: - mesh-catalog modules/docker (image pruning) - mesh-host internal/apply (every new image of a module pulled before any of its containers is replaced; a failed pull applied again on the status view's `settled` for the store's holder, and at the node-engine's start), internal/compose (each routed image reference written with its seat and that seat's holder) - mesh-delivery modules/mesh-grants (what a node-engine hears: the status view's `settled`) -updated: 2026-11-10 +updated: 2026-11-11 decisions: - "ADR 0667" - "ADR 0666" @@ -130,7 +130,8 @@ the verb. ▼ registry seat's holder ── answers the parts it lacks │ the build module sends those parts, in chunks, by `publish` - holder: hashes what it received, asks `builder_versions`, + holder: hashes what it received, asks `builder_versions` + (from milestone G of design 47: `mesh-builds`' `reserved`), stores in its own store, emits `published` or `refused` ▼ builder (durable consumer: entry, done key), indexer, status view, checker -- 2.54.0