diff --git a/00-META/glossary.md b/00-META/glossary.md index 89186fdd..31c8ffc5 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -543,6 +543,11 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel move changed, keeping the composition while no composed fingerprint changed; the verdict names the commits it was carried from (ADR 0548 §5, §6). A head git refuses onto the new commit is not carried: its next check is whole (ADR 0548 §7). +- **try** — the checker's run of a delivery group's check on one or more members, each a repository at an exact + commit, before or without a pull request: the same examiner as the group's check, stricter in that a missing + candidate or a node's missing composition answer ends it as error, and owing the forge no status and no Approve. + It keeps the verdicts of the packages it passed for the check to take (ADR 0637). Asked only when the operator + asks for one (ADR 0637 §6). - **trunk commit** — the commit of the trunk a merge check's verdict was judged against: the trunk's head when the build module cloned it. The verdict holds for that commit only, goes out of date when the trunk moves, and is then carried to the new commit (ADR 0323, ADR 0548). @@ -703,6 +708,10 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel merged head and the group that moved it. A group reaches the mesh as one source revision (ADR 0429 §11); a source revision is **delivered** when every want equals its module's fingerprint and every have equals its want and is healthy, derived from the keys and recorded by no part (§1). +- **pending revision** — a source revision the running indexer has not taken in, because it names another build of + the indexer or follows one that does: only the named indexer's own keys are written, the rest kept in the + indexer's state `pending` until that indexer starts and takes it in (ADR 0633 §1 to §3). A group's members wait + there too until the group's end, as members still gathered (§4). - **canary** — the first node of a module's nodes, ordered by ring then name: a new fingerprint reaches it first and the module's other nodes only once it runs healthy there (ADR 0429 §6). Replaces sending to every machine at once from release 6 (§15); a fingerprint that fails there is held and the canary set back, or for 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 ec98bd03..c08118e2 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 @@ -4,10 +4,18 @@ status: in-progress code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, hq 00-META/checks] updated: 2026-11-05 decisions: + - "ADR 0637" - "ADR 0636" - "ADR 0635" - "ADR 0634" + - "ADR 0633" + - "ADR 0632" + - "ADR 0614" + - "ADR 0603" + - "ADR 0601" + - "ADR 0596" - "ADR 0594" + - "ADR 0593" - "ADR 0572" - "ADR 0571" - "ADR 0570" @@ -60,6 +68,7 @@ decisions: - "ADR 0446" - "ADR 0445" - "ADR 0444" + - "ADR 0441" - "ADR 0437" - "ADR 0435" - "ADR 0434" @@ -602,6 +611,55 @@ From its delivery, a check is this: The assigner writes the verdict under the checker's hash of the heads it judged, in its own bucket, and the checker reads it into the composition of its verdict, which says "not run" until the assigner's module exists (ADR 0528 §5). +### A try: a delivery group's check at exact commits + +*ADR 0637; ADR 0593 §7, §9, ADR 0636 §4, ADR 0548 §5, ADR 0596 §1, §2.* + +A try says beforehand whether a check will pass. It is the check's own code run on commits nobody pushed to a +pull request, and it owes the forge nothing. + +- **A try is a delivery group at exact commits.** The checker's try takes one or more members, each a repository + and the exact commit asked, never a branch's head, with an optional trunk commit. One repository at one commit + is a try of one member, and the same members in any order are one try (ADR 0637 §1). Its steps are check asks + on the build seats' streams, as above. +- **A whole try runs the group's check.** It builds the case a group's pull-request check builds and runs the same + examiner: each member merged onto its trunk, the unit and integration levels on the build modules, the manifest + validator with the seat definitions of the members' commits (ADR 0636 §4), the module graph, and the + composition on every placed node (ADR 0593 §7, §9). It names every failure. A try narrowed to some tests or one + level runs only those, validates no manifest and no composition, and says so (ADR 0637 §2). +- **A try is stricter than the check, on purpose.** When no candidate exists for its members, or a node gave no + composition answer, the try ends as error, never as pass, naming what it could not validate. A check passes such + a node with a warning (ADR 0593 §9); a try cannot, because it would then not say whether the check will pass. A + try that ended as error keeps no verdict (ADR 0637 §3). +- **The indexer makes a try's candidate, on the checker's events.** The checker emits the checker's try events: one + when a whole try starts, carrying its members at their commits, and one when it ends. The indexer, the holder of + the seat `mesh-catalog`, consumes them and writes `candidates.` with one pull per member, its modules and + fingerprints from the previews, validated with the seat definitions the members carry, and deletes it at the + try's end. It reads no state of the checker's; at its start it recovers the tries still open through the + checker's tool that reads a try (ADR 0596 §1, §2, which a record changing ADR 0637 §4 is to carry). The + indexer's tools read and drop a try's candidate. The candidate is the try's alone: the check, the gate and the + forge bridge leave it alone, and the forge is owed no status and no Approve (ADR 0637 §4). +- **A try and a check share kept verdicts.** The try computes each package's digest as the check does and keeps + the verdict of each package it passed under that digest (ADR 0548 §5, ADR 0536 §1). The pull-request check of a + tried head runs only the packages with no kept verdict for their digest, those a moved trunk or a new head + changed, so a group tried before its push costs about one check's worth of build capacity (ADR 0637 §5, ADR 0482 + §1, ADR 0548 §1). +- **No try in the normal flow.** A session asks no try; it asks a group try only when the operator asks for one, + for instance to see a group's composition before its push. The review reports a try asked without the + operator's ask (ADR 0637 §6). +- **Asked through the seat verb from the registry milestone.** The seat verb `try` takes `members` once its seat + definition in mesh-delivery-contracts gives them, at the registry milestone (issue 1966); until then a group try + is asked through the checker's own tool `checker_try`. The names a try shares between the checker and the + indexer, its group name and the two reasons a candidate waits, are defined once in mesh-delivery-sdk from that + milestone, and no copy stays in either module (ADR 0637 §7, §8). +- **How it is checked.** The checker's try test asks two members at exact commits and the same group in any + order, refuses five malformed asks, ends as error without a candidate, keeps no verdict from an error, and fails + naming the node on a refused composition; the examiner's table validates a try's manifests as the check does; + the whole-module test starts a placed engine and a candidate and shows that the check of a tried head runs no + package with a kept verdict; the indexer's take-in table writes and deletes a try's candidate. The registry + group's review finds no copy of the shared names. Live, the registry group is tried whole before its push, and + the checker's try read names each member, the composition per node and every failure (ADR 0637 §9). + ## The Approve and the merge *ADR 0421, ADR 0398 §3, ADR 0406 §1.* @@ -646,6 +704,8 @@ From its delivery, a check is this: revision of `source.main` in one write (ADR 0429 §11). There is no merge window, no batch and no plan: the indexer writes each module's fingerprint at the merge, the builder claims each new fingerprint once, and the 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). - **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. @@ -665,6 +725,79 @@ From its delivery, a check is this: the move; its tools `assigner_wants`, `assigner_want`, `assigner_differences` and `node_haves` read the same keys, and its events `delivered`, `moving`, `settled` and `stuck` say them on the bus. +### Taken in by the indexer it names + +*ADR 0633 with its progressive insights of 2026-11-05, which changes ADR 0603 §1, §4, §6 and §9; ADR 0636 §4, +ADR 0614 §8, ADR 0601 §3, ADR 0632 §2, ADR 0441 §3, §4.* + +A change that brings a new manifest validator is judged by that validator: nothing else of it reaches the +machines until the indexer it builds runs, and the merges behind it then move on the bus's own event, never on a +timer. A group's merges arrive together. + +- **A source revision names its indexer.** The indexer is the module that claims the seat `mesh-catalog`; a source + revision names the build of it whose fingerprint its module has once the revision is applied. The running + indexer learns its own build from the node-engine seat's verb `composition`, and takes a revision in whole only + when the revision names that build. When it cannot learn its own build, it leaves the merged message + unacknowledged, so the bus delivers it again, and says so in its journal (ADR 0633 §1). +- **A revision that names another indexer moves only that indexer.** The running indexer writes only what + building and moving the named indexer needs: the indexer's own module entry, its manifest record, its verdict + (ADR 0614 §8), its key of the module graph and the packages the revision publishes. It keeps the rest in its own + state `pending`, one key per merge carrying the merged message whole, written by compare-and-set, and acknowledges + the merged message only once that key is written. Nothing else of the revision reaches the module, verdict, + graph, taken, predefined, candidate or config-candidate state, so no node-engine, assigner, grants part or builder + reads it (ADR 0633 §1, which changes ADR 0603 §4). +- **Validated with the seat definitions its merges carry.** The running indexer validates the revision's + manifests, the named indexer's own among them, with the seat definitions read from the merges' trees, never a + built-in seat list. When it refuses the named indexer's manifest, the revision stays pending, and the event + `take-in.pending` and the tool `indexer_pending` say so in their field `refused` (ADR 0633 §1, its insight of + 2026-11-05, ADR 0636 §4). +- **The named indexer takes it in at its start.** An indexer that starts as the build a pending revision names + takes the pending revisions in, oldest first, as one take-in judged by its own manifest validator, before it + starts its consumer on the forge bridge's stream; then it removes them from `pending`. It takes in only whole + revisions, and its validation and holds act on them as on any merge (ADR 0633 §2, ADR 0601 §3, ADR 0632 §2). +- **A merge behind a pending revision waits too, and the newest governs.** While a revision is pending, every later + merge joins the pending revisions in merge order, unrelated modules included, because its trees build on theirs; + which indexer they name is worked out over all of them. A later merge that brings the indexer's fingerprint back + to the running build, as a revert does, lets the running indexer take them all in; a pending revision that names + yet another build is taken in by the next indexer only as far as the first bullet allows (ADR 0633 §3, which + changes ADR 0603 §1 and §9). +- **A group's merges are taken in together.** The forge bridge sets the group on each member's merged message, + from the gate's `merges` state, and once every approved member is merged it publishes `forge.group.merged` on + its own stream `forge`, after the last member's merged message, naming the members and their merge commits. The + indexer hears both through its one consumer, keeps each member's merged message in `pending` until the group's + end, and then takes the members in as one source revision under the first bullet. A refused member is no + group's end: the group stays gathered until it merges on, said by the wait below and the urgent condition of a + held group (ADR 0441 §3, §4); the gate passes the group's end by without counting it a refusal. A later merge in + a member's repository waits behind the group; a merge in another repository is taken in alone, unless a revision + is pending for its indexer (ADR 0633 §4, which builds ADR 0429 §11 for take-in). +- **A wait is bounded, said and recoverable.** The indexer's tool `indexer_pending` reads the pending revisions and + the members still gathered, each with its merges, the indexer build it waits for, since when and `refused`; the + view names a module waiting for its revision's indexer through that tool (ADR 0633 §5, which changes ADR 0603 + §6). The indexer emits `take-in.pending` when a revision becomes pending or a group's first member is held, and + `take-in.taken` once it is taken in. The watchman raises the condition `take-in-waits` when no `take-in.taken` + follows within 60 minutes for a revision or 30 minutes for a group, clears it on `take-in.taken`, and at its + start seeds its open waits from `indexer_pending`; the bounds only raise the condition, and nothing is taken in + on a timer. The hand acts, each said in the journal and the audit, are the indexer's `indexer_take_in_pending` + (take the pending revisions, or a group gathered so far, in with the running indexer, when the named indexer can + never run or the group's end never comes) and `indexer_drop_pending` (drop a pending merge a later one replaced), + and the forge bridge's `bridge_groups` (read the groups it holds open) and `bridge_group_end_again` (publish a + group's end again from the gate's `merges` state) (ADR 0633 §5). +- **The check refuses an indexer the running parts could not move.** 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 + when that validator would refuse or hold it: such an indexer would never run, and every revision behind it would + wait for ever (ADR 0633 §6, ADR 0636 §4). +- **How it is checked.** One integration test starts the indexer whole against the bus: a group's two merged + messages and its end are taken in as one revision, nothing written before the end; a revision changing the + indexer writes only the indexer's own keys and shows in `indexer_pending`; restarted as the named build, the + indexer takes every pending revision in, oldest first, with no timer, and a group still gathered stays pending. + Unit tables cover a later merge joining, a revert, another named build, a refused member, a merge in a member's + and in another repository, an unknown own build, a compare-and-set conflict, both events, the watchman's two + bounds and its seeding, the view, the gate passing the end by, and each hand act; three cases cover the seat + definitions of the revision (accepted without a built-in seat list, a refusal in `refused`, the check accepting + the same candidate). Live, the registry group's merge shows in `indexer_pending` waiting for the new indexer and + is taken in when that indexer starts (ADR 0633, How it is checked). + ## A backup before the apply *ADR 0214 §7, ADR 0233, ADR 0404, ADR 0429 §14, ADR 0539 §16.*