diff --git a/03-DESIGN/01-to-be/47-milestones/G-21-provider-and-revert-ask.md b/03-DESIGN/01-to-be/47-milestones/G-21-provider-and-revert-ask.md new file mode 100644 index 00000000..367c3a0a --- /dev/null +++ b/03-DESIGN/01-to-be/47-milestones/G-21-provider-and-revert-ask.md @@ -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 + `` for a seat held once for the mesh, `.` 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...`, 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 on 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 " 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 `.`), so the condition keeper's key is `delivery...revert-held`; + severity `warning`; its summary "The revert of on is held: (, 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 )", 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...` 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` + ` `, `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..` (§467), `` `validate--`, `` the stream sequence of the newest merge the revision + covers and `` 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 + at ". + - **`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.`, 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 ``. 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 ``+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.`, 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..`, 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.` 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. diff --git a/03-DESIGN/01-to-be/47-milestones/G.md b/03-DESIGN/01-to-be/47-milestones/G.md index a71562b7..ae913028 100644 --- a/03-DESIGN/01-to-be/47-milestones/G.md +++ b/03-DESIGN/01-to-be/47-milestones/G.md @@ -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).