Design 47 cites ADR 0639 for the review's rulings, and fixes the indexer's build at its start
The rulings of round 1 now rest on a record, clause by clause, instead of on the review; the indexer's own build is set once from the build it was started with, so a process being replaced cannot take in with its old manifest validator (ADR 0639 §7). Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -4,6 +4,7 @@ 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 0639"
|
||||
- "ADR 0638"
|
||||
- "ADR 0637"
|
||||
- "ADR 0636"
|
||||
@@ -617,12 +618,11 @@ From its delivery, a check is this:
|
||||
|
||||
### 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; the review of
|
||||
2026-11-05 where no record decides.*
|
||||
*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
|
||||
§6, §9.*
|
||||
|
||||
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. Where a statement below rests on the review of 2026-11-05 rather than
|
||||
a record, it says so.
|
||||
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
|
||||
@@ -633,13 +633,13 @@ a record, it says so.
|
||||
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 of some tests
|
||||
keeps no verdict; a try of one level keeps only that level's (the review of 2026-11-05).
|
||||
keeps no verdict; a try of one level keeps only that level's (ADR 0639 §4, which changes ADR 0637 §5).
|
||||
- **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, the node by name.
|
||||
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).
|
||||
- **A whole try that waits for its candidate says why.** Its try read gives the reason it waits, and the wait is
|
||||
bounded by the step bound (the review of 2026-11-05).
|
||||
given within the checker's step bound (ADR 0639 §5).
|
||||
- **The indexer makes a try's candidate, on the checker's events.** For a whole try only, the checker emits
|
||||
`try.started`, carrying the try's id and its members at their commits, and `try.ended`, carrying its id and its
|
||||
state, on its stream `checks`; a narrowed try emits neither, because it makes no candidate. The events' contract
|
||||
@@ -654,11 +654,12 @@ a record, it says so.
|
||||
or events. The forge bridge's read of the gate's `merges` state (*Taken in by the indexer it names*, below) is
|
||||
the same kind.
|
||||
- **A try event is never lost.** The checker publishes each try event with retry until the stream `checks`
|
||||
acknowledges it, and `try.ended` carries the state on every end, also for a try ended by a checker restart. Only
|
||||
the checker's account may publish `try.*` on its stream, a row of the manifest test (the review of 2026-11-05).
|
||||
acknowledges it, and `try.ended` carries the state on every end; whether it was sent is kept in the try's own
|
||||
state, so an end after a checker restart still sends it. Only the checker's account may publish `try.*` on its
|
||||
stream, a row of the checker's manifest test (ADR 0639 §2, which extends ADR 0638 §1).
|
||||
- **At its start the indexer recovers the open tries before it handles the events.** It creates its consumer on
|
||||
the stream `checks` first, then calls the checker's tool `checker_tries`, declared in its `invokes`, which
|
||||
answers every open try, never only the newest, with its members and whether it is whole. It writes a candidate
|
||||
answers every open try, with no bound on the newest, with its members and whether it is whole (ADR 0639 §3). It writes a candidate
|
||||
for each whole open try that has none, deletes every candidate whose try is not open, and only then handles the
|
||||
events it holds. A try that starts or ends between the call and the consumer is heard as an event, and each
|
||||
write is by try id, so a try seen both ways is written once and deleted once (ADR 0638 §3). The candidate is the
|
||||
@@ -666,12 +667,12 @@ a record, it says so.
|
||||
Approve (ADR 0637 §4).
|
||||
- **When `checker_tries` does not answer, nothing is deleted.** The indexer deletes no candidate, its take-in at
|
||||
start goes on, and it asks again on the checker's next try event and by its tool `indexer_recover_tries`; the
|
||||
failure is said in `indexer_try_candidates` until a recovery succeeds. In the release that brings ADR 0638, the
|
||||
failure is said in `indexer_try_candidates` (its state `try-recovery`) until a recovery succeeds. In the release that brings ADR 0638, the
|
||||
new indexer starts before the new checker, so the old checker does not serve `checker_tries` yet: an ordering
|
||||
edge of the module graph (a served verb, ADR 0614), and the reason the failure path is needed at once (the
|
||||
review of 2026-11-05).
|
||||
- **How long it is kept.** The checker keeps an ended try for 7 days, readable by `checker_try_read` (the review
|
||||
of 2026-11-05). How long the stream `checks` keeps its events is not decided here (*What is not decided here*).
|
||||
edge of the module graph (a served verb, ADR 0614), and the reason the failure path is needed at once (ADR 0639
|
||||
§1, which changes ADR 0638 §3).
|
||||
- **How long it is kept.** The checker keeps an ended try's record for 7 days, readable by `checker_try_read`, and
|
||||
then removes it, so its state does not grow with every try (ADR 0639 §6). How long the stream `checks` keeps its events is not decided here (*What is not decided here*).
|
||||
- **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
|
||||
@@ -703,10 +704,12 @@ a record, it says so.
|
||||
deletes it on its end, and recovers at its start (an open whole try gets its candidate, a candidate of a try not
|
||||
open goes, a narrowed try gets none, a try started between the call and the consumer gets one), and the
|
||||
checker's try test shows both events for a whole try, neither for a narrowed one, and `checker_tries` answering
|
||||
the open tries; both manifests are accepted by the manifest validator (ADR 0638 §4). The review's rows: a
|
||||
`checker_tries` that does not answer deletes nothing and is said in `indexer_try_candidates`; a recovery of more
|
||||
than 64 open tries lists them all; a narrowed try keeps only what its narrowing allows; the manifest test
|
||||
refuses `try.*` from any account but the checker's. The registry group's review finds no copy of the shared
|
||||
the open tries; both manifests are accepted by the manifest validator (ADR 0638 §4). The indexer's
|
||||
take-in table also has a recovery that gets no answer (no candidate deleted, the failure in `try-recovery`, the
|
||||
retry on the next try event and by `indexer_recover_tries`); the checker's try test shows the retry until
|
||||
acknowledgement, `try.ended` after a restart, the whole list of open tries (more than 64), the verdicts kept by a
|
||||
try of some tests and of one level, the reason a waiting try gives, and an ended try's record kept 7 days and
|
||||
removed after; the checker's manifest test shows that only its account publishes `try.*` (ADR 0639 §9). 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); during that try `indexer_try_candidates` reads its
|
||||
candidate, and after `try.ended` it is gone (ADR 0638).
|
||||
@@ -778,9 +781,9 @@ a record, it says so.
|
||||
|
||||
### 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, ADR 0564 §2, ADR 0595 §11, ADR 0596 §3, ADR 0615; the review
|
||||
of 2026-11-05 where no record decides.*
|
||||
*ADR 0633 with its progressive insights of 2026-11-05, which changes ADR 0603 §1, §4, §6 and §9; ADR 0639 §7, §8,
|
||||
which change ADR 0633 §1; ADR 0636 §4, ADR 0614 §8, ADR 0601 §3, ADR 0632 §2, ADR 0441 §3, §4, ADR 0564 §2, ADR 0595
|
||||
§11, ADR 0596 §3, ADR 0615.*
|
||||
|
||||
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
|
||||
@@ -788,10 +791,17 @@ 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). It also emits
|
||||
`take-in.pending` with what waits "own build unknown", so the wait's bound covers it (the review of 2026-11-05).
|
||||
indexer takes a revision in whole only when the revision names its own build (ADR 0633 §1).
|
||||
- **The indexer fixes its own build at its start.** It sets its own build once, at its start, from the build it
|
||||
was started with, and never learns it later from the node-engine seat's verb `composition`. A process the
|
||||
node-engine is replacing therefore keeps its old build to the end and cannot take the new revisions in with its
|
||||
old manifest validator; they wait for the new process. An indexer that learnt its build lazily and kept it
|
||||
could, while being replaced, learn the new fingerprint and take in with the old validator (ADR 0639 §7, which
|
||||
changes ADR 0633 §1).
|
||||
- **An indexer with no own build says so on the bus.** It leaves the merged message unacknowledged, so the bus
|
||||
delivers it again, says so in its journal, and sends `take-in.pending` with the reason `own build unknown` for
|
||||
the revision it holds, so the watchman's bound for `take-in-waits` covers that wait (ADR 0633 §1, ADR 0639 §8,
|
||||
which changes it).
|
||||
- **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
|
||||
@@ -839,8 +849,8 @@ timer. A group's merges arrive together.
|
||||
(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). Both of the indexer's hand acts emit
|
||||
`take-in.taken` for what they took in or dropped, so the condition clears (the review of 2026-11-05).
|
||||
group's end again from the gate's `merges` state) (ADR 0633 §5). A hand take-in emits `take-in.taken` as any
|
||||
take-in does, so the condition clears (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
|
||||
@@ -854,7 +864,8 @@ timer. A group's merges arrive together.
|
||||
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
|
||||
the same candidate); and rows for an own build fixed at start (a composition answer that differs changes
|
||||
nothing) and for `take-in.pending` with `own build unknown` (ADR 0639 §9). Live, the registry group's merge shows in `indexer_pending` waiting for the new indexer and
|
||||
is taken in when that indexer starts, and no node-engine reads distribution's new entry before the new indexer's
|
||||
hold: `node_manifest_reads` and `indexer_holds` on every node (ADR 0633, How it is checked).
|
||||
|
||||
@@ -1514,8 +1525,6 @@ R4d, the three image reworks (route-proxy, nats, automx; n8n left by ADR 0506),
|
||||
## What is not decided here
|
||||
|
||||
- How long the checker's stream `checks` keeps its events (*A try*, above).
|
||||
- How the old indexer takes in while it is replaced by a new one: written once the review's reading of the
|
||||
build is in.
|
||||
- Who is granted `checker_try` and the take-in hand tools: the operator's grants, outside this design.
|
||||
- A web view of deliveries beyond the pull request's comment.
|
||||
- Whether `mesh/delivery-group` is required on the trunks: the operator's setting, through the forge module's
|
||||
@@ -1526,19 +1535,19 @@ R4d, the three image reworks (route-proxy, nats, automx; n8n left by ADR 0506),
|
||||
|
||||
The amendment of 2026-11-05 (ADR 0637, ADR 0638, and ADR 0633 with its insights of 2026-11-05) was reviewed by the
|
||||
four reviewers by architectural domain side by side (playbook 01), and the lead ruled on every finding in round 1.
|
||||
Where a ruling decides what no record does, the text says "the review of 2026-11-05". What was found, and what
|
||||
The rulings that no record decided became ADR 0639, which the text cites clause by clause. What was found, and what
|
||||
answers it:
|
||||
|
||||
| Domain | Found | Answered by |
|
||||
|---|---|---|
|
||||
| seams | the indexer said to read none of the checker's state, while it reads the checker's previews and the checker watches its candidates; the forge bridge's read of the gate's `merges` unmarked | *A try* (two reads that stay under ADR 0596 §3 until issue 1966); *Taken in by the indexer it names* (the bridge's read marked the same) |
|
||||
| tools | the try's tools not listed against the four lines; the hand acts not said to clear the condition | *A try* (the table of the checker's and the indexer's tools, `indexer_recover_tries` new); *Taken in* (the hand acts emit `take-in.taken`) |
|
||||
| tools | the try's tools not listed against the four lines; the hand acts not said to clear the condition | *A try* (the table of the checker's and the indexer's tools, `indexer_recover_tries` new); *Taken in* (a hand take-in emits `take-in.taken`; a hand drop is open) |
|
||||
| bus forms | the form of `pending` and the try events' contract unsaid | *Taken in* (`pending` in the old form the grants part creates); *A try* (the package `tryevent` of mesh-delivery's sdk) |
|
||||
| running qualities | `checker_tries` not answering; the new indexer before the new checker in this release; a lost try event; what a narrowed try keeps; a recovery cut at 64; how long tries are kept; a try waiting for its candidate | *A try* (nothing deleted, asked again, said in `indexer_try_candidates`, the ordering edge; publish with retry, `try.ended` on every end; the narrowed try's verdicts; every open try listed; 7 days; the reason in the try read, bounded by the step bound) |
|
||||
| testing | ADR 0633's live case on the node-engines' reads lost; no live case for ADR 0638 | *Taken in* (`node_manifest_reads` and `indexer_holds` on every node); *A try* (the candidate read during the registry group's try, gone after `try.ended`) |
|
||||
| health and events | an own build unknown left unbounded; who raises `take-in-waits`; what the two take-in events carry | *Taken in* (`take-in.pending` "own build unknown"; the watchman's finding and the condition keeper, ADR 0564 §2; the events' body) |
|
||||
| security | any account could publish a try event; a node's missing composition answer | *A try* (only the checker's account publishes `try.*`, a manifest-test row; the try ends as error naming the node, keeping no verdict) |
|
||||
| upgrade | the old indexer's take-in during its replacement | open: *What is not decided here*, written after the developer's reading |
|
||||
| upgrade | the old indexer's take-in during its replacement: a build learnt lazily and kept let a process being replaced take in with its old validator | *Taken in* (the indexer fixes its own build at its start, never later from `composition`) |
|
||||
|
||||
Left as notes, not in this amendment: the grants of `checker_try` and of the hand tools (the operator's), and the
|
||||
test budget of the checker and the indexer.
|
||||
|
||||
Reference in New Issue
Block a user