Design 47 follows ADR 0637 and ADR 0633: a group try, and a revision taken in by its own indexer
A try now checks a delivery group at exact commits with the check's own code, and a merge's take-in waits for the indexer its revision names, so design 47 says both where its checks and its source revision are described. The indexer hears a try on the checker's events rather than reading its state (ADR 0596); the record changing ADR 0637 §4 to say so is to follow. The glossary gains try and pending revision. Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.<try id>` 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.*
|
||||
|
||||
Reference in New Issue
Block a user