diff --git a/00-META/glossary.md b/00-META/glossary.md index b8dd1927..c14e2624 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -1120,8 +1120,8 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272, 0383, 03 (ADR 0286). It covers a module's events and streams too (the operator, 2026-11-11; design 34 every-event §32): a module may mark one of its events, a restricted stream is marked whole, and a `mesh_wait` on a marked event runs only on the operator's warrant for that one wait, with the exact names it waits on and its bound, which the tool - runner asks and checks before it subscribes. The tool runner reads every mark from the manifests and the seat list - on the bus (design 46 7-approval-mark §5). + runner asks and checks before it subscribes. The tool runner reads every mark from the catalog half on the bus + (design 46 7-approval-mark §5). - **operator message / input** — what arrives on `intake`, once the router has checked the sender against its own list of the operator's identities: a **trusted** one is an operator message, addressed to an agent by `@name` or thread or else to the **responder** (`@mesh`, the router's own diff --git a/03-DESIGN/01-to-be/34-milestones/every-event.md b/03-DESIGN/01-to-be/34-milestones/every-event.md index f3b705ba..186f8131 100644 --- a/03-DESIGN/01-to-be/34-milestones/every-event.md +++ b/03-DESIGN/01-to-be/34-milestones/every-event.md @@ -121,8 +121,11 @@ that holds its mark reach the machines together. ## The tool runner (module `node-tools`) -11. **Its manifest loses `waits`.** `node-tools/module.json` drops the key `waits` and its five entries; nothing else - in it changes, its `version` stays `2` (ADR 0670 §2). +11. **Its manifest's changes, every one.** `node-tools/module.json` drops the key `waits` and its five entries (ADR + 0670 §2); gains `operator-channel` in `uses`, for the asks of marked waits (§35; design 46 7-approval-mark §7); gains + in `requires` the state `approvals` of design 46 7-approval-mark §13; and gains the stream `node-tools-findings` and + the `consumes` of `mesh-conditions.findings-asked` that design 47 G §387 writes. Its `version` stays `2`, a label + no reader judges. Nothing else in it changes. 12. **The publication time is kept.** `bus.Heard` gains `Published`, read from the event's header `x-time`, which mesh-sdk's emit sets (`go/process/emit.go`) and the status view's own emit too; `HearFirst` reads it from the message's headers, and leaves it empty for an event that carries none. @@ -135,11 +138,12 @@ that holds its mark reach the machines together. named by `buscompose.EventName` of the subject it arrived on. The timeout, the filter and the timed-out answer are unchanged (ADR 0367 §1). The refusal of names outside a list, and its words naming the manifest, go (ADR 0670 §2). 14. **A misspelt name is refused, not waited on for ever.** Before it subscribes, `waitAnswer` refuses a name whose - emitter is neither a seat of the seat list the tool runner reads through the seat `mesh-catalog` nor a module of - the indexer's manifests it holds as a first view (`mesh-delivery-indexer.views.manifests`), naming both sources. + emitter is neither a seat nor a module of the catalog half the tool runner holds from G (its first view by + `mesh-catalog.revision`, followed on `catalog-revision`; design 47 G §196), naming that source and its revision. A name whose emitter is known but whose event no seat definition and no manifest's `emits` declares is waited on, - and its timed-out answer says that no declaration names that event. While either source is not yet read (the - first views' state, `mesh_first_views`), names are not checked, and the answer says `unchecked` with why. + and its timed-out answer says that no declaration names that event. While the catalog half is not yet read (the + first views' state, `mesh_first_views`), names are not checked, and the answer says `unchecked` with why; a + marked wait is then held as §34 says. 15. **The answer names the heard event.** A heard event is answered as today, `heard` with `subject`, `body` and `at`, where `subject` is now the heard event's whole name (`mesh-delivery-view.delivered`, `operator-channel.decided.claude-code`), with two new fields: `from`, `seat` or `module`, the namespace it @@ -176,8 +180,9 @@ that holds its mark reach the machines together. a name with `*`, with `>`, of one token, 17 names, an emitter no source knows (§14), and a bus-refused subscription answered after the timeout with §16's words; `TestMeshWaitWithNoFamiliesIssuedWaitsOnNothing` is deleted. In `bus/wait_test.go`, `TestTheBusRefusesAWaitOutsideTheGrant` stays as §16's refusal test, with a timeout of a - fraction of a second, its comment citing ADR 0670 §1. No test function is added: the module stays within its - budget. + fraction of a second, its comment citing ADR 0670 §1. The manifest test also holds each change of §11: no `waits`, + `operator-channel` in `uses`, the state `approvals` in `requires` with its options, and the finding stream. No test + function is added: the module stays within its budget. ## The claude-code module's Stop hook @@ -190,8 +195,9 @@ that holds its mark reach the machines together. 21. **The prepare lists every event.** The milestone file's prepare (playbook 07, step 0) gains a section *Events and their fields*: every event each module's manifest `emits` and each seat's definition declares, read from the - indexer's modules (`mesh-delivery-view.indexer_modules`) and the seat definitions (`mesh-catalog.seat-definitions`), - each a holder's event with its subject form; and every event mesh-sdk publishes on a module's behalf (the + manifests and the seat definitions at the group's heads, not only from what the live bus holds, so the events G + and design 46's milestone 7 add are listed (the finding streams of design 47 G §383 among them), each a holder's + event with its subject form; and every event mesh-sdk publishes on a module's behalf (the announcement's `states` on `mesh.mod..event.states`, found by the callers of its subject builders); each with the fields of its body as the emitting code builds it, by file and line (ADR 0670 §4). 22. **The code review confirms none carries a secret, before the grant is delivered.** The milestone's code review @@ -242,8 +248,10 @@ that holds its mark reach the machines together. ## Delivery -28. **One delivery group, no order named between its members.** The members are pushed and checked as one group. No - order is named between them, and no transitional field is kept (the operator, 2026-11-03: a change is designed as +28. **In G's delivery group, no order named beyond the module graph's.** The members are pushed and checked in design + 47 milestone G's group, with design 46's milestone 7, whose edges (design 46 7-approval-mark §23) order the gate + before the seat definition that marks `execute`; this milestone adds no order of its own between its members, and no + transitional field is kept (the operator, 2026-11-03: a change is designed as it should be, its delivery is the delivery system's): a part built with the new mesh-sdk reads a stored manifest of the tool runner that still carries `waits` without it (ADR 0578 §3), and a node-engine is given a want only for a manifest whose words it reads (ADR 0614 §4). @@ -253,7 +261,7 @@ that holds its mark reach the machines together. that it was issued no event families. Both are loud. A watcher reads its owner after every answer (ADR 0367 §4) and ends at a call failed twice (its definition), so neither becomes a loop; a laptop asleep through the window gets its new tool runner when it wakes, and its watchers read their owners meanwhile in the same way. -30. **Going back is going forward.** A fault met after the delivery is fixed by a new change delivered the same way. +30. **Going back is going forward** (design 46 7-approval-mark §24 for the gate's part). A fault met after the delivery is fixed by a new change delivered the same way. Reverting the group instead reverts every member, the indexer first, since the manifest validator delivered here sets aside the old tool runner's manifest carrying `waits` (§10); until the revert reaches a node, `mesh_wait` there answers as §29 says. @@ -288,22 +296,30 @@ approves, for a marked event or a restricted stream. `events` carry, naming the event, as ADR 0286 §4 refuses a mark on a tool the module does not serve (design 46 7-approval-mark §4's table gains the row). A module that marks only events needs neither `operator-channel` in `uses` nor `approval-mark/1`: the tool runner asks for the wait, not the module. -33. **The tool runner reads the marks from the manifests on the bus.** The tool runner derives each module's marked - subjects from the indexer's manifests it already holds as a first view (`mesh-delivery-indexer.views.manifests`, - §14): for a marked event `` of module ``, `mesh.mod..event.` and - `mesh.stream..event.`, each event pattern as the bus spells it (`WirePattern`); for a stream with a - `grant`, every subject of `ModuleStream.Subjects()`. It derives them again on every change of that view, and no - module's name is written in its code; it is design 46 7-approval-mark §5's derivation, extended to events. *Why:* +33. **The tool runner reads the marks from the catalog half on the bus.** The tool runner derives each module's marked + subjects from the catalog half it holds (§14; the catalog graph entry carries `needs-approval` and each stream's + `grant`, design 46 7-approval-mark §5): for a marked event `` of module ``, + `mesh.mod..event.` and `mesh.stream..event.`, each event pattern as the bus spells it + (`WirePattern`); for a stream with a `grant`, every subject of `ModuleStream.Subjects()`; and each of these also + followed by `.>`, since an event may name its node or asker after its name (design 47 G §383) and + `ModuleStream.Subjects()` answers only the patterns the stream stores. It derives them again on every revision it + hears, and no module's name is written in its code; it is design 46 7-approval-mark §5's derivation, extended to + events, following the running version as that line says. A mark added while a wait waits applies from the next + call, which that call's answer says. *Why:* ADR 0286 §5 had the mark issued in a membership; no controller issues one now, a membership names no event (ADR 0670 §2), and the judged manifest on the bus keeps the mark out of any bundle as §5 required (the operator, - 2026-11-11: the mark read from the manifests on the bus); §14 already reads this view. + 2026-11-11: the mark read from the manifests on the bus); from G the catalog half is where the tool runner reads + which modules exist (design 47 G §196). 34. **Which wait is marked.** A wait is marked when any subject it subscribes (§13, §26, after `from`) overlaps a marked subject of §33, that is, some subject matches both, a wildcard on either side. mesh-sdk's `events.go` (§1) - gains `Overlaps(a, b)` for it. While the manifests view is not yet read (`mesh_first_views`), a wait with any - subject outside `mesh.seat.` is marked and its refusal says the marks are not yet read, since a mark that cannot - be read runs nothing (ADR 0286 §40; design 46 7-approval-mark §19); a wait `from: seat` is never marked. + gains `Overlaps(a, b)` for it. Before the catalog half is read (`mesh_first_views`), a wait with any subject + outside `mesh.seat.` waits, within its own timeout, for the first view and is then judged; nothing is asked while + the marks are unread, and a wait still unjudged when its timeout ends is refused then, saying the marks are not + yet read, since a mark that cannot be read runs nothing (ADR 0286 §40; design 46 7-approval-mark §19). A wait + `from: seat` is never marked. 35. **A marked wait runs only on the operator's warrant for that one wait.** The call is an act as design 46 - 7-approval-mark §8 makes one: the address `mesh_wait`, the node, the module `node-tools`, its build, and every + 7-approval-mark §8 makes one: the address `mesh_wait`, the node, the module `node-tools` and the tool `mesh_wait`, + its build (the digest of the tool runner's own artifact), and every argument given (`subjects`, `from`, `filter`, `timeout`), so the warrant binds the exact names, the filter and the bound. The tool runner refuses, asks, judges and claims it as that part's §9, §10, §11 and §13 to §17 do for a marked tool, asking on the seat `operator-channel` under its own module's name, `node-tools`, whose manifest gains @@ -317,18 +333,24 @@ approves, for a marked event or a restricted stream. shown with the exact names it waits on and its bound. 36. **A refused marked wait is said, and never a hot loop.** A marked wait with no warrant the tool runner accepts subscribes to nothing. Its first call publishes the ask at once; every such call answers, as an error, design 46 - 7-approval-mark §12's or §17's words with "this wait" for "the call", only after its timeout or its caller's going, as §16 - answers a refused subscription. *Why:* ADR 0286 §14 answers within 5 seconds, which a watcher's loop would turn - into a call every few seconds for the ask's hour; a call made again after the operator's Approve, within 30 - minutes of it, runs. An unmarked wait is heard as §13 to §16 say, with no ask. -37. **The tests hold it.** §19's table of refusals in `console/wait_test.go` gains rows, no test function: a wait on a - fixture module's marked event, and one on a fixture stream with a `grant`, each with no warrant, subscribes - nothing, asks once and answers ADR 0286 §37's words after its timeout; the same wait with a warrant bound to its - exact names, filter and timeout subscribes and answers the event published after it, so the restricted stream's - event is heard only after the warrant; with a warrant for other names, another filter or another timeout it is - refused; the same names `from: seat` ask nothing; and before the manifests view is read, a module's name is - refused as §34 says. They run on the tool runner's test bus (`meshtest`), the warrant written there as the - router's record. `Overlaps` gains rows in §1's table (`mesh.stream.mail.event.mail.>` overlaps + 7-approval-mark §12's or §17's words with "this wait" for "the call", when its timeout ends or its caller goes, as + §16 answers a refused subscription, or as soon as the router's record of its ask says it was chosen: the wait hears + the warrant event `operator-channel.decided.node-tools` for its ask's id and then reads the record, an event only + waking it, and answers at once, so the same call made again runs within the warrant's 30 minutes. *Why:* ADR 0286 + §14 answers within 5 seconds, which a watcher's loop would turn into a call every few seconds for the ask's hour; + ending at the choice keeps a long wait from spending the warrant's window. An unmarked wait is heard as §13 to §16 + say, with no ask. +37. **The tests hold it.** Unit, as the budget counts them (`meshtest` runs its bus in the test's process): §19's table + of refusals in `console/wait_test.go` gains rows, no test function: a wait on a fixture module's marked event, and + one on a fixture stream with a `grant`, each with no warrant, subscribes nothing, asks once and answers design 46 + 7-approval-mark §12's words after its timeout; a wait with the most timeout a wait takes (ten minutes, ADR 0367 + §1), its ask chosen at its second minute, answers at the choice, and the same call made then runs; the same wait + with a warrant bound to its exact names, filter and timeout subscribes and answers the event published after it, + so the restricted stream's event is heard only after the warrant; with a warrant for other names, another filter + or another timeout it is refused; the same names `from: seat` ask nothing; a wait named `..` + under a marked `.` is marked; and before the catalog half is read, a marked wait asks nothing, is + judged when the first view comes within its timeout, and is refused at its timeout when it does not. The warrant is + written on the test bus as the router's record. `Overlaps` gains rows in §1's table (`mesh.stream.mail.event.mail.>` overlaps `mesh.stream.mail.event.mail.*.message`; `mesh.mod.mail.event.x` does not overlap `mesh.stream.mail.event.x`), and §32's refusal a row in the indexer's `judge/rules_test.go`. @@ -353,4 +375,8 @@ approves, for a marked event or a restricted stream. `mesh-delivery-checker.verdict` wakes on the checker's next verdict (§25, §26; ADR 0671 §1). - **The grant does not follow a restricted stream's grant value** (integration on a real bus): §27's cases pass (ADR 0671 §2). -- **A marked wait runs only on the operator's warrant** (unit, and on the test bus): §37's rows pass. +- **A marked wait runs only on the operator's warrant** (unit): §37's rows pass. +- **A wait on mail, as the operator sees it** (live, read by tool call and on the phone): a session's `mesh_wait` on + `mesh-mail.mail`, the mail module's restricted stream, asks "Wait on mesh-mail.mail on ?" on the phone with + its bound, and hears nothing before; it ends refused when its timeout ends or as soon as the operator chooses; the + same call made after the Approve hears the next mail, and the next call asks again (§32 to §36). diff --git a/03-DESIGN/01-to-be/34-the-console.md b/03-DESIGN/01-to-be/34-the-console.md index f62a5522..6861be17 100644 --- a/03-DESIGN/01-to-be/34-the-console.md +++ b/03-DESIGN/01-to-be/34-the-console.md @@ -143,7 +143,7 @@ reads the state once from the verb that owns it: an event only wakes it (ADR 036 flowchart LR E[a seat's, a module's or a module stream's event
mesh.seat.<seat>.event.… / mesh.mod.<module>.event.… / mesh.stream.<module>.event.…] -->|heard for the call| TR[the tool runner's account] G[seat mesh-grants] -->|grants the three event subjects| TR - M[the indexer's manifests] -->|the marked events and restricted streams| TR + M[the catalog half, seat mesh-catalog] -->|the marked events and restricted streams| TR TR -->|a marked wait only on the operator's warrant| OC[seat operator-channel] TR -->|mesh_wait answers it by name| S[a session] S -->|then reads the state| O[the verb that owns it] @@ -169,7 +169,7 @@ needs no `consumes` and no entry in that value, and keeps no consumer of it. **A marked event is heard only on the operator's warrant** (the operator, 2026-11-11). The approval mark (ADR 0286) covers a module's events and streams as well as its tools and verbs: a module may mark one of its events in `needs-approval`, and a restricted stream is marked whole. The tool -runner reads the marks from the indexer's manifests on the bus, and a `mesh_wait` on a marked event, or on any subject +runner reads the marks from the catalog half on the bus (seat `mesh-catalog`), and a `mesh_wait` on a marked event, or on any subject of a marked stream, runs only on the operator's warrant for that one wait, asked on the seat `operator-channel` with the exact names it waits on and its bound, and checked before the tool runner subscribes; without one it is refused and says so. The bus grants the subjects, and the tool runner's own gate holds the mark, as the operator chose; the diff --git a/03-DESIGN/01-to-be/46-milestones/7-approval-mark.md b/03-DESIGN/01-to-be/46-milestones/7-approval-mark.md index e921e7e7..9bed17e6 100644 --- a/03-DESIGN/01-to-be/46-milestones/7-approval-mark.md +++ b/03-DESIGN/01-to-be/46-milestones/7-approval-mark.md @@ -8,12 +8,14 @@ decisions: - "ADR 0286" - "ADR 0259" - "ADR 0627" + - "ADR 0656" - "ADR 0666" - "ADR 0668" - "ADR 0595" - "ADR 0597" - "ADR 0310" - "ADR 0268" + - "ADR 0564" --- # Design 46, milestone 7 — a call that needs the operator's approval @@ -21,78 +23,100 @@ decisions: **What it builds.** A tool or a seat verb marked in its manifest or its seat's definition runs only on the operator's approval of that one call, with its exact arguments (ADR 0286). The first call asks the operator on the seat `operator-channel` and runs nothing; the same call made again within 30 minutes of the operator's Approve runs once. -The tool runner (module `node-tools`) asks, judges and claims; the mark is read from the manifests and the seat list on -the bus. The shape is in the design's main file, +The tool runner (module `node-tools`) asks, judges and claims; it reads the marks from the catalog half on the bus; and +on a machine whose tool runner does not honour the mark the grants part withholds every marked subject. The shape is in +the design's main file, [§10, *A call that needs the operator's approval*](../46-the-conversation-with-the-operator.md#a-call-that-needs-the-operators-approval); this part holds every line the milestone builds, cited as "design 46 7-approval-mark §n". -**When.** It is built and delivered in design 47's milestone G, in one delivery group with G and with design 34's -milestone every-event, whose marked waits (design 34 every-event §32 to §37) use this gate (the operator, 2026-11-11: -"Build the gate in G"). +**When.** It is built and delivered in design 47's milestone G, in G's delivery group, with design 34's milestone +every-event, whose marked waits (design 34 every-event §32 to §37) use this gate (the operator, 2026-11-11: "Build the +gate in G"). -**Where.** mesh-sdk (the manifest field, the seat definition's field, the seat list), mesh-contracts (the login shell's -seat definition), mesh-delivery (the manifest validator in the indexer), mesh-tools (the tool runner's gate, its state, -its tool) and mesh-catalog (the zsh module, which holds the login shell). Nothing is built in a controller: no -controller issues memberships today, bus permissions are the grants part's (seat `mesh-grants`), the hand-act log is -the seat `mesh-hand-acts`, and the asker's logic is operator-channel's client in mesh-contracts (ADR 0627 §1). +**Where.** mesh-sdk (the manifest field, the seat definition's field, the per-machine state's keys), mesh-contracts +(the login shell's seat definition), mesh-delivery (the indexer's manifest validator and catalog graph entry, the grants +part's deny), mesh-tools (the tool runner's gate, its state, its tools) and the mesh MCP server's `mesh_describe` in +it. Nothing is built in a controller: no controller issues memberships today, bus permissions are the grants part's +(seat `mesh-grants`), the hand-act log is the seat `mesh-hand-acts`, and the asker's logic is operator-channel's client +in mesh-contracts (ADR 0627 §1). -**What changes from ADR 0286 as written.** Each line below says in its *Why* where something built since ADR 0286 -(2026-10-10) changes how a clause is met: the mark is read from the bus and not from a membership (§5), a module is held -off a runner that does not honour it by the capability hold and not by its bus permissions (§6), the digest is Go alone -(§8), the claim is kept in the runner's bus state and not a file (§13), `warranted` is a kept call and not a retry on -a timer (§16), and a fourth open ask is offered as G makes it (§17). The controller's own verbs (ADR 0286 §43) are -not built: no controller serves verbs. +**What changes from ADR 0286 as written.** Each line says in its *Why* where something built since ADR 0286 +(2026-10-10) changes how a clause is met: the mark is read from the catalog half and not from a membership (§5), the +digest is Go alone (§8), the claim is kept in the runner's bus state and not a file (§13), `warranted` is a kept call +and not a retry on a timer (§16), and a fourth open ask is offered as G makes it (§17). The controller's own verbs (ADR +0286 §43) are not built: no controller serves verbs. + +**The bound of the gate.** The gate holds for every call that reaches the tool runner's handler. It does not hold for +a process that reads the tool runner's bus credential (the file `broker` in its state directory, `own-secrets`) and +speaks to the bus itself: its account may subscribe a marked subject on a machine whose runner honours the mark. Who +may read that file is therefore the bound of the operator's choice; the prepare reads its mode on every machine with +`/node-reads.stat` and writes it in the milestone file, and a mode readable beyond the operator's account is a +design question for the operator before develop. The same limit as design 47 G §385 (one tool-runner account per +machine). ## The mark 1. **A module marks its tools and verbs in `needs-approval`.** mesh-sdk's `catalogue.Manifest` gains `NeedsApproval`, the manifest key `needs-approval`: a list of strings, each a tool the module serves (``) or a seat verb it serves (`.`), and, under design 34 every-event §32, one of its events. Absent is no mark. *Why:* ADR - 0286 §1, §49 (each marking is a manifest change, reviewed like any other). *How it is checked:* a row in the - decoder's table test in `go/catalogue` decodes the key, and the manifest reads baseline gains `word:needs-approval`. + 0286 §1, §49 (each marking is a manifest change, reviewed like any other). *How it is checked:* unit, a row in the + decoder's table test in `go/catalogue`; the manifest reads baseline gains `word:needs-approval`. 2. **A seat's definition marks a verb for every holder.** mesh-sdk's `seatdef` reads a verb's `needs-approval`, a - boolean, in a seat definition (`seats//seat.json` in mesh-contracts), and the seat list it reads into - (`seatlist`, read with `mesh-catalog.seat-definitions`) carries it on the verb. *Why:* ADR 0286 §2; the seat list - is where the tool runner already reads the seats it holds verbs of. *How it is checked:* a fixture row in - `seatdef`'s table test, and one in `seatlist`'s. + boolean, in a seat definition (`seats//seat.json` in mesh-contracts); the seat definitions the catalog half + carries (design 47 G §194) carry it on the verb. *Why:* ADR 0286 §2. *How it is checked:* unit, a fixture row in + `seatdef`'s table test. 3. **Marks add up.** A verb is marked when its seat's definition or its holder's `needs-approval` marks it; a holder - cannot unmark what its seat marks. *Why:* ADR 0286 §3. *How it is checked:* a row of §5's table: a seat-marked verb - with no entry in the holder's manifest is marked. + cannot unmark what its seat marks. *Why:* ADR 0286 §3. *How it is checked:* unit, a row of §5's table. 4. **The manifest validator refuses a mark it cannot honour.** The indexer's manifest validator (`judge/rules.go`) refuses, as the checker runs it, and sets aside to wait, as the indexer runs it, a manifest that: marks a tool its `tools` does not list, or a verb its `claims` do not serve (ADR 0286 §4); marks a tool or a verb, or holds a seat - verb its seat marks, without `operator-channel` in `uses` (§7) or without `approval-mark/1` in `capabilities` (§6); or - marks a tool or a verb and declares a resource of type `process`, whose tools its own program answers and no tool runner - gates. Each refusal names the entry and the rule. *Why:* a mark nothing enforces would be a call that runs unasked - while saying it is guarded; ADR 0286 §43 had the controller enforce its own verbs, and no other own-process server - has a gate, so such a mark is refused rather than ignored (the design's choice under ADR 0286 §4, §40). *How it is - checked:* one row per refusal in `judge/rules_test.go`, and a row that accepts the zsh module's manifest of §21. + verb its seat marks, without `operator-channel` in `uses` (§7); marks a tool or a verb and declares a resource of + type `process`, whose tools its own program answers and no tool runner gates; or names in `needs-approval` one name + that is both one of its tools and one of its events. Each refusal names the entry and the rule. *Why:* a mark + nothing enforces would be a call that runs unasked while saying it is guarded; ADR 0286 §43 had the controller + enforce its own verbs, and no other own-process server has a gate, so such a mark is refused rather than ignored; + and an entry that names two things is read one way by one reader and another way by the next (the design's choices + under ADR 0286 §4, §40). *How it is checked:* unit, one row per refusal in `judge/rules_test.go`, and a row that + accepts the zsh module's manifest of §21. -## Where the tool runner reads it +## Where the marks are read, and where they are withheld -5. **From the manifests and the seat list on the bus, never from a bundle.** The tool runner derives, for each module - it serves, the set of its marked tools and verbs from the indexer's manifests it consumes as a first view - (`mesh-delivery-indexer.views.manifests`) and from the seat list it reads (`seatlist`), again on every change of - either, in a package of its own, `node-tools/internal/approval`. No module's name is written in its code. *Why:* - ADR 0286 §5 had the controller issue the mark in a membership so that a bundle could not drop it; no controller - issues memberships now, and the judged manifest on the bus is the same guarantee: the bundle never carries the mark. - *How it is checked:* a table test in `approval` derives the marks from fixture manifests and a fixture seat list, - §3's row among them. -6. **A marked module runs only where the runner honours the mark.** The tool runner states `approval-mark/1` among - its call capabilities (`runtime.CallCapabilities`), which its announcement carries and its node's node-engine takes - into the node's record; a module whose manifest asks for `approval-mark/1` in `capabilities` (§4) is held off a - machine whose runner does not state it, as every capability is (ADR 0595 §9, ADR 0597). *Why:* ADR 0286 §7 and §8 - withheld each marked tool from a runner that did not honour the mark, by leaving its subject out of that runner's - bus permissions; the capability hold, built since, keeps the module from running there at all, and the grants part - grants a module's tools as `mesh.mod..tool.>` whole, so no subject is left out (ADR 0286 §9 has nothing to - name apart). *How it is checked:* the existing capability test of `CallCapabilities` gains the entry; and in §22's - whole test a marked fixture module is not loaded by a runner built without the capability. +5. **From the catalog half on the bus, never from a bundle.** The indexer's catalog graph entry of a module (design 47 + G §174) gains its `needs-approval` and, for each stream it declares, whether the stream carries a `grant`; the seat + definitions of the catalog half carry the verb marks (§2). The tool runner, which from G takes its first view of the + catalog half by `mesh-catalog.revision` and follows `catalog-revision` (design 47 G §196), derives from it, for each + module it serves, its marked tools and verbs, in a package of its own, `node-tools/internal/approval`, again on + every revision it hears. No module's name is written in its code. The marks follow the running version: the runner + applies the marks of the module's version it serves, and, while the newest revision's entry for that module + differs, the union of both, so a mark is never lost before or after the module moves. A mark added while a call + waits applies from the next call, which the answer of §12 says. *Why:* ADR 0286 §5 had the controller issue the mark + in a membership so that a bundle could not drop it; no controller issues memberships now, and the judged catalog + half is the same guarantee: the bundle never carries the mark (ADR 0656 §6, one source for which modules exist). + The union keeps ADR 0286 §3's "marks add up" across a version change. *How it is checked:* unit, a table test in + `approval` derives the marks from fixture catalog halves, §3's row, a version change and the union among them. +6. **A machine whose tool runner does not honour the mark serves no marked tool or verb.** The tool runner states + `approval-mark/1` among its call capabilities (`runtime.CallCapabilities`), which its announcement carries and its + node's node-engine takes into the node's record; the assignment half carries it among the capability readings + (design 47 G §194). The grants part, composing a runtime's account, reads that runtime's node's readings: where the + tool runner does not state `approval-mark/1`, it composes a subscribe deny of every marked subject of every module + the runtime carries (`mesh.mod..tool.`, the same followed by `.*`, and for a marked verb + `mesh.seat..tool.`, the same followed by `.`), which the bus's deny outranks its allow of + `mesh.mod..tool.>` for. `Permissions` gains `SubscribeDeny`, written into the user list as the server's + subscribe deny, as `PublishDeny` is for publishing. The withheld tools and verbs are named in the grants part's read + of that account (`mesh-grants.grants_user_list`, a field `withheld` with each subject and "its tool runner does not + state approval-mark/1"), and in the node-engine's `composition` of the module. *Why:* ADR 0286 §7, §8, §9: the bus + enforces the mark; the runner's status of ADR 0286 §7 is read from the grants part's account read, since a runner + that does not honour the mark is a version that cannot say so. *How it is checked:* unit, the grants part's + composition table: with readings lacking the capability the deny of each marked subject is composed and named in + `withheld`, with it no deny; integration, the grants part's real-bus test: an account composed with the deny is + refused delivery of a call on a marked subject while it holds `mesh.mod..tool.>`. 7. **The asker's grants are a user's of `operator-channel`.** Because the manifest says `operator-channel` in `uses` (§4), the grants part composes, on the account of the runtime carrying the module, the publish of `mesh.seat.operator-channel.accept.ask.` and `.cancel.`, the hearing of its warrants on `mesh.seat.operator-channel.event.decided.` and the read of the router's record under `.` in the - seat's state `asks`, as for every user (ADR 0259 §3). Nothing new is composed for the mark. *Why:* ADR 0286 §6. - *How it is checked:* a row in the grants part's composition table test for a fixture module with a mark. + seat's state `asks`, as for every user (ADR 0259 §3). The runner passes the client its own publish and read on that + runtime's account (`Client.Publish`, `Client.Record`), never the bundle's. *Why:* ADR 0286 §6. *How it is checked:* + unit, a row in the grants part's composition table for a fixture module with a mark. ## The act, and the first call @@ -102,12 +126,12 @@ not built: no controller serves verbs. argument given, a string as itself and any other value as its JSON in RFC 8785's canonical form; an argument left out is not in the act. Its digest is `operatorchannelclient.ActDigest`. *Why:* ADR 0286 §10, §11; the digest is in mesh-contracts since ADR 0627 §1. The TypeScript twin ADR 0286 named is not built: the tool runner is Go, and no - TypeScript asker computes an act. *How it is checked:* table rows in `approval` for the act's fields, an omitted + TypeScript asker computes an act. *How it is checked:* unit, rows in `approval` for the act's fields, an omitted argument, a number, an object and a changed build each giving another digest. 9. **A call refused without asking.** On a call with no approval for its act, the runner refuses at once, asking nothing, a call whose arguments hold anything `outward.CheckSecret` refuses (mesh-sdk), or whose arguments are more than 16 KiB. *Why:* ADR 0286 §12; mesh-sdk's `outward` is the content rule ADR 0310 §4 moved there. *How it is - checked:* two rows of §22's refusal table. + checked:* unit, two rows of §22's table. 10. **One ask, under the module's name.** Otherwise the runner asks through an `operatorchannelclient.Client` whose `Module` is the marked module and whose `State` is the runner's state of §13, unless an ask for the same act is open there (§13's `act.` key), in which case it asks nothing new. The ask: `about` the act's digest; `who` @@ -117,103 +141,138 @@ not built: no controller serves verbs. whole; the explanation: the first line of the tool's own description, the node, that it was called through the mesh's tools, which cannot tell who called, every argument one per line as "name: value", and the act's fingerprint, its digest's first 24 hexadecimal digits in groups of four. *Why:* ADR 0286 §13, §15, §16, §20 to - §23; the client checks the ask (`Check`) before it publishes. *How it is checked:* a test in `approval` holds the - published ask's fields against a fixture call, and a second identical call publishes nothing. + §23; the client checks the ask (`Check`) before it publishes. *How it is checked:* unit, a row in `approval` holds + the published ask's fields against a fixture call, and a second identical call publishes nothing. 11. **An argument is shown whole only where it may leave the mesh.** Each argument is shown whole where `outward.Check` passes it, else in its shape with ‹path›, ‹address› or ‹withheld› in place of what it refuses; the arguments are shown whole when they are at most 2000 bytes and 40 lines, otherwise their first 20 lines and 1200 bytes, marked "NOT SHOWN IN FULL", and the explanation then says that the whole is read with `mesh_approvals` (§18) on that node, whose fingerprint must be the one on the phone. *Why:* ADR 0286 §17 to §19. *How it is - checked:* rows of §10's test with a path, an address and a long argument. -12. **The first call answers "Not run".** The runner waits up to 5 seconds for the router's word on the ask (its - record, §14) and answers the call as an error, never as a result: "Not run: this needs the operator's approval. - Asked on at