Design 47 takes in round 1 of the review by architectural domain
The try and the take-in described seams, failure paths, live cases and the health chain less exactly than the release builds them: the reads that stay until issue 1966, the bus form of pending and the tryevent contract, the try's tools against the four lines, a checker_tries that does not answer, try events never lost, what a narrowed try keeps, the finding and the condition keeper, and both live cases. Rulings no record decides are marked as the review's. Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -11,12 +11,15 @@ decisions:
|
||||
- "ADR 0634"
|
||||
- "ADR 0633"
|
||||
- "ADR 0632"
|
||||
- "ADR 0615"
|
||||
- "ADR 0614"
|
||||
- "ADR 0603"
|
||||
- "ADR 0601"
|
||||
- "ADR 0596"
|
||||
- "ADR 0595"
|
||||
- "ADR 0594"
|
||||
- "ADR 0593"
|
||||
- "ADR 0590"
|
||||
- "ADR 0572"
|
||||
- "ADR 0571"
|
||||
- "ADR 0570"
|
||||
@@ -614,10 +617,12 @@ 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, §2.*
|
||||
*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.*
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
- **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
|
||||
@@ -627,26 +632,46 @@ pull request, and it owes the forge nothing.
|
||||
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).
|
||||
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).
|
||||
- **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).
|
||||
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).
|
||||
- **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`. The indexer, the holder of the seat `mesh-catalog`, consumes them: on
|
||||
state, on its stream `checks`; a narrowed try emits neither, because it makes no candidate. The events' contract
|
||||
is the package `tryevent` of mesh-delivery's sdk, read by the checker and the indexer, so no other module
|
||||
rebuilds for it (ADR 0638 §1). The indexer, the holder of the seat `mesh-catalog`, consumes them: on
|
||||
`try.started` it 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 on `try.ended` it deletes it. It reads no
|
||||
state of the checker's (ADR 0638 §1, §2, which change ADR 0637 §4; ADR 0596 §1, §2). A narrowed try emits
|
||||
neither event, because it makes no candidate.
|
||||
previews, validated with the seat definitions the members carry, and on `try.ended` it deletes it. It no longer
|
||||
reads the checker's state `tries` (ADR 0638 §2, which changes ADR 0637 §4; ADR 0596 §1, §2).
|
||||
- **Two reads across the seam stay until issue 1966 moves them.** The indexer reads the checker's previews, and the
|
||||
checker watches the indexer's `candidates.<try id>`. Both are readers of another module's bus state that ADR 0596
|
||||
§3, as ADR 0615 changes it, lets stay until their module moves; issue 1966 tracks the move to the owner's tools
|
||||
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).
|
||||
- **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 the open tries with their members and whether each is whole. 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 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).
|
||||
answers every open try, never only the newest, with its members and whether it is whole. 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
|
||||
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).
|
||||
- **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
|
||||
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*).
|
||||
- **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
|
||||
@@ -660,16 +685,31 @@ pull request, and it owes the forge nothing.
|
||||
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).
|
||||
- **The try's tools.** Beside the seat verb `try`, the checker's and the indexer's own tools, against the four
|
||||
lines a module's tools answer:
|
||||
|
||||
| Line | The checker | The indexer |
|
||||
|---|---|---|
|
||||
| read everything it holds | `checker_try_read` (one try: its members, steps, composition per node, failures, why it waits), `checker_tries` (every open try), `checker_kept` (the kept verdicts by digest) | `indexer_try_candidates` (each try's candidate, and a failed recovery) |
|
||||
| do by hand what it does itself | `checker_try` (ask a try) | `indexer_recover_tries` (recover the open tries now) |
|
||||
| recover without help | `checker_try_end` (end a try, which emits `try.ended`) | `indexer_drop_try_candidate` (drop a try's candidate), `indexer_recover_tries` |
|
||||
| one seat, many tools | the seat verb `try` from the registry milestone | the seat `mesh-catalog`'s verbs, unchanged |
|
||||
|
||||
- **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 a try's candidate on its start, 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 (ADR 0638 §4). 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).
|
||||
package with a kept verdict (ADR 0637 §9). The indexer's take-in table writes a try's candidate on its start,
|
||||
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
|
||||
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).
|
||||
|
||||
## The Approve and the merge
|
||||
|
||||
@@ -739,7 +779,8 @@ pull request, and it owes the forge nothing.
|
||||
### 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 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.*
|
||||
|
||||
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
|
||||
@@ -749,12 +790,15 @@ timer. A group's merges arrive together.
|
||||
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).
|
||||
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).
|
||||
- **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,
|
||||
the merged message only once that key is written. `pending` is in the old form of bus state, the bucket the
|
||||
grants part creates, not a template in `requires`; it moves when the indexer next moves its bus state (ADR 0595
|
||||
§11 as ADR 0615 changes it). 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
|
||||
@@ -773,7 +817,8 @@ timer. A group's merges arrive together.
|
||||
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
|
||||
from the gate's `merges` state, a read of another module's bus state that ADR 0596 §3 lets stay until issue 1966
|
||||
moves it, 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
|
||||
@@ -785,14 +830,17 @@ timer. A group's merges arrive together.
|
||||
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-in.taken` once it is taken in, both ordinary events of the indexer; each carries the wait's key, what
|
||||
waits (a revision with the indexer build it waits for, or a group with its members so far), since when, and
|
||||
`refused` (ADR 0633 §1, §5). The watchman writes the finding `take-in-waits` when no `take-in.taken` follows
|
||||
within 60 minutes for a revision or 30 minutes for a group, and the condition keeper raises the condition from
|
||||
it and clears it on `take-in.taken` (ADR 0564 §2 as ADR 0590 §2 changes it). At its start the watchman 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).
|
||||
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).
|
||||
- **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
|
||||
@@ -807,7 +855,8 @@ timer. A group's merges arrive together.
|
||||
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).
|
||||
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).
|
||||
|
||||
## A backup before the apply
|
||||
|
||||
@@ -1464,7 +1513,32 @@ 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
|
||||
protection tool.
|
||||
- How a builder's public SSH key reaches the forge: the forge's and the SSH client's mechanism.
|
||||
|
||||
## Reviewed by architectural domain
|
||||
|
||||
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
|
||||
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`) |
|
||||
| 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 |
|
||||
|
||||
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