Merge pull request 'Design 47 G: §708 to §715, a requirement's provider, the revert ask, the validation before take-in and non-trunk branches' (#573) from design/47-G-AS14-AS17 into main

This commit was merged in pull request #573.
This commit is contained in:
2026-11-12 09:48:23 +00:00
2 changed files with 259 additions and 2 deletions
@@ -0,0 +1,252 @@
---
layer: to-be
status: in-progress
design: 47
milestone: G
updated: 2026-11-12
decisions:
- "ADR 0666"
- "ADR 0656"
- "ADR 0633"
- "ADR 0579"
- "ADR 0571"
- "ADR 0564"
- "ADR 0539"
- "ADR 0330"
- "ADR 0259"
- "ADR 0126"
- "ADR 0112"
- "ADR 0084"
---
# 47, milestone G: the provider a requirement points to, the ask of a held revert, the edge an acting rule gives, and the take-in of every watched branch
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".
*§708 to §710 answer the assigner's develop questions on build-map rows AS14 and AS17 (2026-11-12), which the lead's
answer A-167 ([G-20](G-20-lead-answers.md)) sent to a design line: which field names the seat whose holder provides a
requirement, which field says a requirement carries contributed values and the edge's kind word (§357), and the form
of the assigner's ask when a revert is held (§300). §711 answers the indexer's develop question on build-map row IX8
(2026-11-12), which the lead's answer A-212 (G-22, the lead's second answers file) sent to a design line: which acting steps
give an edge of kind 5 (§47), between which entries, and what a catalog entry's `acts` holds. §712 to §715 answer
the indexer's develop questions on rows IX5 to IX7 (2026-11-12), which A-213 sent to design lines: the validation's
run before take-in, what holds a pending revision while it runs, and how a watched branch other than the trunk gets
its merges, its pending revision and its groups. Each is the design's
choice within the record it cites, tested against [the principles](../../../00-META/principles.md).*
## The provider a requirement points to
- §708 **The named seat is the seat that delivers the requirement's provision; no catalog entry field names it.**
(ADR 0330 §2; ADR 0126 §3; ADR 0084 §3, §4; ADR 0571 §4; design 27 *A module provider*) This completes §357's
order for the assigner's `Compute`. A catalog entry's `requires` stay bare provision names. For each requirement
R of a placed module on node N, in this order:
1. **The named seat's holder.** The seat whose definition's field `delivers` (mesh-sdk `seatdef`) is R, among the
seat definitions the entry's catalog graph revision names in its `definitions` (each read whole through the
seat `mesh-catalog`'s verb `seat-definitions`, as every part reads the catalog half). Its holder is read from
the same revision's placements, from their `holds`, as the assigner's state `seat-holders` keeps them: the key
`<seat>` for a seat held once for the mesh, `<seat>.<N>` for a node seat. A seat that delivers R and that no
placement holds holds the entry, naming the seat; no other step is tried (design 27, step 1).
2. **A pin.** The placed module's `bindings` entry for R, else N's placement `pins` entry for R.
3. **The provider on N.** The one module placed on N whose catalog entry's `provides` names R.
4. **The only provider.** The one module placed on any node whose catalog entry's `provides` names R.
Each step that answers gives the entry the edge of kind `provisioning`, its `node`, `module` and `fp` the
provider's and its `provision` R. Where no step answers and more than one candidate is placed, the entry carries
`held`, a list with one item per requirement held: `requirement` R, `seat` (the unheld seat, step 1 only) and
`candidates`, each `{node, module}`, sorted; the assigner writes no want from a held entry, so the node keeps what
it runs, and the field is read with the entry through the seat `mesh-wants`' verbs. An optional requirement
(ADR 0330 §3) with no candidate is left absent: no edge, no hold. A hard requirement with no candidate is held
with `candidates` empty. Why: the seat definition already carries `delivers`, and the assigner already keeps who
holds which seat from the placements, so nothing new is declared and no part outside the assigner's inputs is read
(ADR 0656 §2; principles 1 and 7); a hold is said in the graph, never only in a log (principle 3; design 47 G
A-167). How it is checked: unit (assignment graph), one row per step, the seat's holder winning over a provider
on N, an unheld delivering seat holding the entry with `seat` named, two candidates and none on N held with both
named, an optional requirement with none left absent.
- §709 **A requirement carries contributed values where the manifest's `contributes` names it; its edge is of kind
`contribution`.** (ADR 0656 §2; §174, §175, §357, §442) This completes §357's contribution edge. The catalog entry
gains `contributes`: the requirement names the manifest's `contributes` object has as keys, sorted, its local
names not carried; the indexer fills it, since it alone reads a manifest (§442). `requires` keeps naming every
requirement, those only `contributes` names included, as mesh-sdk's `catalogue.Manifest.Wants` lists them, since
contributing to a requirement is requiring it. For each requirement in `contributes` that §708 resolves, the
consumer's entry carries, beside its `provisioning` edge, an edge of kind `contribution` to the same provider, its
`provision` the requirement. The kind word is `contribution`, the glossary's and the seat `mesh-wants`' `Edge`
`kind` (provisioning, contribution or acting); the assigner's `EdgeContribution` joins `EdgeProvisioning` and
`EdgeActing`. The `Edge` fields' descriptions in the seat `mesh-wants`' definition become: `provision`, for
provisioning and contribution, the requirement; `seat`, for acting only, the seat whose rule gave the edge. A seat
contribution (the manifest's `contributions`, a piece given to a seat's holder on the node) gives no edge in G.
Why: the manifest already says which requirements carry contributed values, and the edge kind already has its word
in the glossary and the contract (principles 7 and 8). How it is checked: unit (indexer), a manifest whose
`contributes` names `route` gives an entry whose `contributes` and `requires` both name it; unit (assignment
graph), that entry gets a `provisioning` and a `contribution` edge to the route's provider, and an entry with no
`contributes` gets none of kind `contribution`.
## The ask of a held revert
- §710 **The assigner asks the operator about a held revert as it asks about a failed backup.** (ADR 0539 §16;
ADR 0259 §3, §4; ADR 0564 §2; §297, §300, §363, §461, §541, §700, §706) This completes §300. When a node's have
says the phase `held` for a revert, with the reason `restore point missing` (§300, a missing point or no
`restored`) or `restore point damaged` (§567), the assigner:
- **Asks**, through mesh-sdk's `operatorchannelclient` on the seat `operator-channel`, as its other asks are made,
an ask of the new kind `revert`, kept in its state `asks` under `revert.<node>.<module>.<fingerprint hex>`, the
fingerprint the reverted build's; like the backup ask it needs no proof of who answered, so the view may answer
it. Its headline: "The revert of <module> on <node> has no restore point". Its explanation names the reverted
fingerprint, the kept data, its form and the node's reason in its own words, and says "<module> keeps running
its newer build until you answer. Started on the live data, the older build reads data written in the newer
form." Two options, each of level acknowledge: `hold`, "Keep it held", does "the node keeps running the newer
build, and the revert waits"; `live`, "Start on the live data", does "the node starts the older build on the
live data as it is, with nothing restored". It expires after the assigner's `AskLasts`, `OnExpiry` "the module
keeps running its newer build". The answer is read from the router's record of the ask (§700).
- **Raises the condition**, through mesh-sdk's finding writer (§461), with the finding `revert-held` (scope
`delivery`, id `<node>.<module>`), so the condition keeper's key is `delivery.<node>.<module>.revert-held`;
severity `warning`; its summary "The revert of <module> on <node> is held: <reason> (<kept data>, form <form>);
the ask about it waits for your answer"; its `needs` "Nothing for you to do.", so the operator's one message is
the ask (§706).
- **On the answer**: `live` writes the node's want again with the want's new field `revert` `live` and `words`
"the operator answered \"live\" to the revert ask (asking <n>)", as the backup's answer is written; the node
agent, reading `revert` `live` from its own want and from nothing else, starts the reverted build with no
`restore` and no aside taken, calls no catch-up tool, and applies as any want. `hold` writes nothing: the hold
stands. The finding is cleared once the reverted build runs, or when a newer want of that module on that node
replaces the held one, which also drops the ask (§363). An ask that ends unanswered is raised again at once
while the hold stands; one answered `hold` is not asked again.
- **Its tools**: `assigner_read_asks` reads the ask and `assigner_read_findings` the finding, both existing;
`assigner_ask_again` raises it again by hand.
Why: ADR 0539 §16 has the assigner ask, raise the condition and write the want again naming the answer for a hold
before an apply, and the backup ask is that shape already built (principle 7); the finding writer is the one way
G's parts raise a condition (§383); the ask alone reaches the operator, as O-9 had it for §706 (principle 3, the
hold is still loud in the condition). How it is checked: whole (assigner, a fixture node agent saying `held` with
`restore point missing`): the ask under `revert.<node>.<module>.<hex>` with both options and the finding raised;
`hold` writes no want and the newer build keeps running; `live` writes the want with `revert` `live` and its
words, and the finding clears once the have names the reverted build; a newer want drops the ask and clears the
finding; an unanswered ask raised again. Unit (node-engine): a want with `revert` `live` starts the reverted build
with no `restore` call; unit (`meshwantsclient`): a `revert` other than `live` is refused.
## The edge an acting rule gives
- §711 **An acting rule gives an edge of kind 5 from the acting part to each entry it acts on, in the steps that act on
a delivered version.** (ADR 0656 §1; ADR 0666 §14, §19; §47, §49, §203, §265, §266, §270, §324, §374, §378, §535,
§569, A-208) This completes §47's row 5 for the indexer, which writes a catalog entry's edges (A-208).
- **The entry's `acts`.** The indexer fills a catalog entry's field `acts` when it makes the entry: for each seat
its manifest's `claims` names, each acting rule that seat's definition carries, among the definitions the entry's
catalog graph revision names in its `definitions` (read through the seat `mesh-catalog`'s verb
`seat-definitions`, as §708), as one item `{seat, step, on, bound}`: `seat` the claimed seat, `step`, `on` and
`bound` as the definition writes them, `bound` left absent where the rule names none (its step's default, §266,
is the reader's). Items sorted by `seat`, then `step`; a module that claims no seat with a rule has no `acts`. It
is what the runner (§535) and the builder (§546) read, and what the edges below are derived from; nothing else
writes it. The contract's description of `step` gains `pipeline` (§203).
- **The steps.** `grant`, `compose`, `place` and `check`, and from G's delivery `pipeline` in `check`'s place
(ADR 0666 §19; §65), each give kind 5: a grant or a credential, a composition, a placement, a verdict, each made
for the dependent's new version (ADR 0666 §14). `build` gives kind 3 (§47, §267), never kind 5. `take-in` and
`validate` give no edge: an entry is in a revision only once the indexer has taken it in and validated it, so
such an edge would always be met (principle 7).
- **Between which entries.** For each entry B with such a rule, and each other entry A of the same revision that
the rule's `on` matches (`every`; a language, A having an artifact of that language; `kind:image`, A having an
artifact of kind image, §324): one edge `kind` 5, `from` B's delivered artifact, `to` A's delivered artifact (the
artifact the entry's first process resource runs, else its first, as kinds 4 and 6 have it), `word` `acts`, `why`
`<seat> <step>`, `stale` false (it only orders, §378), `declared` false. No edge goes to B itself, nor to an entry
B's new version is built from or needs: one with a path of edges of kinds 1 to 4 to B in that revision (a
package, a fetched artifact, a build module or base, a provider), which B's running version acts on (§270). An
entry with no artifact has no edge. A rule of a seat no entry claims gives none; the step's own holder says that
(§233, §535).
- **How it is met.** The edge is met when B's new version has acted for A's build, as the holder's `wait-ended`
says and §374 records; for `compose` per node, by the assigner (§268). It is a delivery edge, so a cycle of it
with kinds 2, 4 and 6 only is delivered as one step, and one with kind 1 or 3 is refused naming it (§49, §569).
Why: the rule already says the step and the entries, and the artifact a delivery orders is the one kinds 4 and 6
already name, so no manifest word is added and no module is named in code (principles 7 and 8); a cycle left by a
rule is refused loudly at the check (principle 3). How it is checked: unit (indexer's graph), an entry claiming a
seat with rules `grant` on `every` and `build` on `go` carries both in `acts` with its seat; a grants part of rule
`grant` on `every` gives kind 5 to every other entry but none to itself nor to the build module and package it is
built from; a rule `compose` on `kind:image` gives edges only to entries with an image artifact; a rule `build`
gives no kind 5; an entry with no rule has no `acts`.
## The validation before take-in, and the take-in of every watched branch
- §712 **The validation is one `run` of the indexer's manifest validator over two cases, refusing only what the revision causes.**
(ADR 0666 §5, §6; ADR 0579 §2; ADR 0578 §2; §16, §206, §338, §354, §467, §481, §493, A-213) This completes §16's ask.
For a pending revision of watched branch B (§713), the running indexer asks the seat `node-build-go` the accepted
kind `run`, on `mesh.seat.node-build-go.accept.run.indexer` (§481; its callers §338), its step key
`indexer.<node>.<id>` (§467), `<id>` `validate-<s>-<n>`, `<s>` the stream sequence of the newest merge the revision
covers and `<n>` the count of asks for it, from 1. Its job:
- **`program`**: the indexer's own program `cmd/mesh-judge`, under the Go module the `package` of the indexer
entry's Go artifact names, at that entry's `path`, built at the revision's head of that entry's `repository`;
the indexer entry is the entry of B's revision being validated that claims the seat `mesh-catalog`, else, when
B watches no indexer, the trunk's current revision's. No repository or module name is fixed in code (ADR 0666
§5); the program's own directory is the indexer's own code.
- **`heads`**: none, since the manifest validator reads only its request; **`args`**: none; **`reader`** "manifest validator of
<B> at <s>".
- **`input`**: the manifest validator's request, served by the seat `mesh-catalog`'s verb `input` and kept in `inputs` as §354
has it, any size (ADR 0666 §16): case `change`, every module of B's current revision by its entry's manifest,
each one the pending merges carry replaced by the merged message's manifest bytes, one added for a new
directory; case `trunk`, every module of B's current revision as it stands (the name is the validator's, not the
branch); `seats` the definitions the merges' trees carry (the main file, *Validated with the seat definitions
its merges carry*); `packages` and `predefined` as the take-in's own manifest validator has them; no `machines`.
- **Its verdict.** The revision is refused when the case `change` refuses a manifest the revision adds or changes
(its digest differs from the `trunk` case's) with a problem the `trunk` case lacks, or names a problem `(between
manifests)` the `trunk` case lacks (ADR 0579 §2). A problem only in unchanged manifests refuses nothing; their
entries keep their verdicts (ADR 0579 §1). A refusal sets the pending value's `refused` to each manifest and its
problems, said in `take-in.pending` (§493).
Why: the manifest validator already answers two cases for exactly this rule, as the checker ran it, and the build seat's `run`
is the one way a program built from heads runs (principle 7; ADR 0666 §6). How it is checked: whole (indexer, a
fixture Go build module): a revision whose changed manifest gains a problem stays pending, `refused` naming it; a
revision whose unchanged manifest has one is taken in; the job names the indexer entry's repository, path and the
revision's head, and no heads.
- §713 **A pending revision names its ask, and resumes on the ask's end; a run that could not validate is no
refusal.** (ADR 0666 §6, §18, §24; ADR 0633 §5; §16, §18, §19, §24, §269, §483, A-213; the main file *A wait is
bounded, said and recoverable*) This completes §16's wait, and amends the main file's *A revision that names another
indexer moves only that indexer* in that every revision now waits there for its validation. Every merge of a watched branch is first written to
`pending` as the main file has it (key `m.<sequence>`, or a group's key, §715), its value gaining `validation`:
`ask` (the `run` ask's id), `asked` (when) and `input` (its digest). The indexer puts the ask, acknowledges the
merged message, and the take-in ends there. It hears the end through a durable consumer of its own of the seat
`node-build-go`'s event `step-ended` (§24), taking only ends whose step key starts with `indexer.`, and reads the
answer from that step key; at its start it reads each pending value's `validation` and its step key, so an end said
while it was down is not lost. An end whose ask is not the one the pending value names now is dropped, written in
its journal. A later merge of B while an ask is out joins the pending revision (the main file) and is asked anew
with the next `<n>`. On the end:
- **accepted**: the revision is taken in as the main file has it, by the indexer it names (§16), and its keys go;
- **refused**: it stays pending with `refused` (§712), asked again only as part of a later merge of B, as a
revert is (§19);
- **could not validate**: the step ended `could not run`, the program exited other than 0, or its answer holds not
both cases: it stays pending with `validation`'s new field `failed`, the build seat's words, said in
`take-in.pending`, never as a refusal and never as passed (ADR 0666 §18; principle 3). It is asked again by a
later merge of B and by the indexer's new tool `indexer_validate_again` (key; class `act`, §391's default callers,
written in the hand-act log), which also reads the ask's state.
An ask no build node takes waits on the work queue, said by `build-capacity-waits` after 60 minutes (§483); every
case is timed by `take-in-waits` at 60 minutes (§19). `indexer_pending` shows `validation` whole. Why: a take-in that
holds no call open survives a restart from its own state, and only an answer the manifest validator gave refuses (principles 1,
3 and 4). How it is checked: whole (indexer): a restart while the ask is out takes the revision in on the end; a
`could not run` end leaves it pending with `failed` and `indexer_validate_again` asks `<n>`+1; an end of a replaced
ask is dropped.
- §714 **Every watched branch's merges come on the forge bridge's stream, each naming its branch.** (ADR 0666 §6,
§11; §11, §13, §18, §493, A-90, A-93, A-208) This completes A-208's per-branch take-in. The forge bridge publishes
the merged message of every landed commit of every watched branch the registrations name, heard on `branch-moved`
and read by the seat `git`'s verb `branch` as §11 has it, the trunk's included, since from G no merge is the gate's
(ADR 0666 §11). The merged message (`forgemsg.Forge`) gains `base`, the watched branch it landed on; the forge
bridge's filters to the trunk alone (its merged message and its forge-made merge) go. The indexer's `pending` keeps
its key `m.<sequence>`, the stream's sequence being one across branches, and its value names `base`; a merge waits
only behind a pending value of its own `base` (§18), and is taken in by A-93's take-in of that branch. A value
written before G names no `base` and is the trunk's, the only branch the forge bridge published before G. The
gate's `source-moved` takes nothing in. Why: one take-in for every branch, from the stream every merge already
comes on, and no second source of the same merges (principles 1 and 7). How it is checked: whole (forge bridge,
indexer): a merge on `beta` publishes a merged message with `base` `beta`, pending behind a pending `beta` revision
while a `main` merge is taken in.
- §715 **A group gathers per watched branch, and its end names that branch.** (ADR 0666 §13; ADR 0633 §5; §42 to
§46, §293, §714, A-208) This completes §43 and §44 for a branch other than the trunk. A group's members on watched
branch B are those whose `base` in the seat `mesh-delivery`'s verb `groups` is B (its `Member` carries `base`). The
forge bridge sets the group on each landed member's merged message of any watched branch, and publishes
`forge.group.merged` (`forgemsg.ForgeGroupEnd`, gaining `base`) once `groups` says every member with that `base`
has landed, once per `base`. The indexer gathers the members in `pending` under `g.<group>.<base>`, each part
escaped as a key name, and takes them in as one source revision of B on that end, after §712's validation; a key
`g.<group>` written before G is the trunk's. `indexer_pending`, `indexer_take_in_pending`, `indexer_drop_pending` and
`bridge_group_end_again` take the `base`; a group that never ends on B is said at 30 minutes (§45). Why: a group
is delivered on the branch its members land on, read from the one verb that knows its members (principles 1
and 7). How it is checked: whole (forge bridge, indexer): two members landing on `beta` give one `forge.group.merged`
with `base` `beta` and one `beta` source revision; a group whose `beta` member never lands raises `take-in-waits`
at 30 minutes.
+7 -2
View File
@@ -3,7 +3,7 @@ layer: to-be
status: in-progress
design: 47
milestone: G
updated: 2026-11-11
updated: 2026-11-12
decisions:
- "ADR 0500"
- "ADR 0673"
@@ -72,6 +72,10 @@ decisions:
- "ADR 0259"
- "ADR 0235"
- "ADR 0233"
- "ADR 0330"
- "ADR 0126"
- "ADR 0112"
- "ADR 0084"
---
# 47, milestone G — every line milestone G builds
@@ -82,7 +86,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 eighteen files, each read whole, its design lines numbered across them:
The part is twenty-one 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.
@@ -104,6 +108,7 @@ The part is eighteen files, each read whole, its design lines numbered across th
- [G-18-fix-round-six-seven](G-18-fix-round-six-seven.md): the fix round of §587 to §676: the approval mark once no verb names its callers, the keeper's snapshots, the recovery module's verbs, the node-engine's kept entries, the gate before a marked verb's running, develop's D-16 to D-49, and a delivery's wait on another machine's network (§677 to §705).
- [G-19-outside-wait-and-refusals](G-19-outside-wait-and-refusals.md): a delivery's wait on an outside cause, written whole once (§706, in place of §705), and the finding writer's own refusals in every bundle (§707, amending §580).
- [G-20-lead-answers](G-20-lead-answers.md): the lead's one-line answers to develop's questions, each tested against the principles (A-1 on).
- [G-21-provider-and-revert-ask](G-21-provider-and-revert-ask.md): the provider a requirement points to, the named seat and the contribution edge (§708, §709, completing §357), and the assigner's ask of a held revert (§710, completing §300), the edge an acting rule gives (§711, completing §47's kind 5), the validation before take-in (§712, §713, completing §16) and the take-in and groups of every watched branch (§714, §715).
- [G-22-lead-answers-2](G-22-lead-answers-2.md): the lead's answers continued, from A-182.
Design 34's milestone [every-event](../34-milestones/every-event.md) and design 46's milestone [7-approval-mark](../46-milestones/7-approval-mark.md) ship in milestone G's release, built in its repositories beside G's lines (lead-47, 2026-11-11 15:30 CET); where one of their lines and a line of G differ, G's later line holds and the other is corrected in place (§624 to §626).