Build the approval mark in milestone G and let a marked wait use it

The operator answered "Build the gate in G" and wants no new records: the
marked-wait lines now hold by the operator's answers, each departure said in
its Why, and design 46's milestone 7 becomes a part a builder reads whole,
written to the mesh as built (marks read from the bus, the capability hold,
the client's claim in bus state, the hand-act seat), delivered with G and
every-event.

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 02:41:11 +01:00
parent aec610a6f4
commit ca1fbf7867
5 changed files with 298 additions and 74 deletions
+5 -4
View File
@@ -1117,10 +1117,11 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272, 0383, 03
- **approval mark** — a tool's or verb's declaration in its manifest (`needs-approval`) that each call to it runs
only on the operator's warrant for that one call, with its exact arguments; the tool runner that serves it, or
the controller for its own verbs, asks and checks
(ADR 0286). It covers a module's events and streams too, once the record extending it is accepted (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.
(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).
- **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
+39 -32
View File
@@ -36,12 +36,11 @@ for the playbook and agent lines (§24). Only these reference the list today: no
part's account composition and the tool runner's manifest and manifest test too; its lead rebases it onto this
milestone's heads when G's develop restarts.
**Pending lines.** The lines of *A marked event waits for the operator's warrant* (§32 to §37) write the operator's
answer of 2026-11-11 that a wait respects the approval mark, and are marked *Pending the record that extends the
approval mark*: the mark is ADR 0286's, which marks tools and verbs and has the bus enforce it, and the answer extends
it to events and streams and has the tool runner's own gate enforce it, so a record decides that before these lines
are built (playbook 02, *Answering a design question*). Only §25 leans on them: it grants the tool runner a
restricted stream's subjects, which §32 to §37 gate.
**The approval mark on events.** *A marked event waits for the operator's warrant* (§32 to §37) writes the operator's
answers of 2026-11-11: a wait respects the approval mark, and the gate that asks and checks it (design 46's milestone
7, [`7-approval-mark.md`](../46-milestones/7-approval-mark.md)) is built in design 47's milestone G and delivered in one
group with G and this milestone ("Build the gate in G"). So §25's grant of a restricted stream's subjects and the gate
that holds its mark reach the machines together.
## The event subjects, in mesh-sdk
@@ -225,7 +224,9 @@ restricted stream's subjects, which §32 to §37 gate.
§4) decides who consumes it, not who hears it, so changing or withdrawing that value changes nothing in the tool
runner's grant (ADR 0671 §2); a module that consumes a restricted stream is composed as today (ADR 0376 §2 as ADR
0671 §2 reads it). The bus does not hold the approval mark on a restricted stream; the tool runner's own gate does,
before it subscribes for a wait (§32 to §37; the operator, 2026-11-11).
before it subscribes for a wait (§32 to §37; the operator, 2026-11-11), and that gate is delivered in the same
group as this grant (design 46 7-approval-mark §23). *Why:* ADR 0286 §8 has the bus hold a mark, and the bus
cannot grant a subscription for one approved wait alone, so the operator chose the tool runner's gate.
26. **A name hears its stream form.** `EventSubjects` (§1) answers six subjects, adding
`mesh.stream.<emitter>.event.<rest>` and the same followed by `.>`; `from` (§13) takes `stream` too; `EventName`
answers a stream subject with namespace `stream`. A seat's holder event kept in its holder's stream is named by its
@@ -271,10 +272,12 @@ restricted stream's subjects, which §32 to §37 gate.
## A marked event waits for the operator's warrant
*Pending the record that extends the approval mark: every line of this section. The operator, 2026-11-11, on the
wait of any event: "can it also respect any 'requires approval' config on the topics?"* The tool runner's asking,
judging and claiming for a marked call (ADR 0286 §10 to §42, design 46's milestone 7) are not built yet; which
milestone builds them first is a question open with the operator, and these lines use them as ADR 0286 states them.
*By the operator's answers of 2026-11-11: "can it also respect any 'requires approval' config on the topics?", and
"Build the gate in G".* These lines use the gate design 46 7-approval-mark builds for a marked call (its §8 to §19),
delivered in the same group. *Why they depart from the records, said once for the section:* ADR 0286 §1 marks tools
and verbs, and the operator extended the mark to a module's events and streams; ADR 0671 §1 and §3 have every session
hear every module stream, mail included, with no exception, and the operator narrowed that to a wait the operator
approves, for a marked event or a restricted stream.
32. **A module marks an event, and a restricted stream is marked whole.** A module's manifest `needs-approval` (ADR
0286 §1) may also name one of its own events, by the name its `emits` or one of its `streams`' `events` gives it,
@@ -282,35 +285,40 @@ milestone builds them first is a question open with the operator, and these line
is marked whole, every event it carries, with no entry in `needs-approval`; a stream without a `grant` is not, a
work-queue one included. A seat's definition marks no event. The indexer's manifest validator refuses, as the
checker runs it, a manifest whose `needs-approval` names an event that neither its `emits` nor its streams'
`events` carry, naming the event, as ADR 0286 §4 refuses a mark on a tool the module does not serve.
`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 `<event>` of module `<module>`, `mesh.mod.<module>.event.<event>` and
`mesh.stream.<module>.event.<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. The mark is so read from the judged manifest, never from a bundle (as ADR
0286 §5 asks of a tool's mark) and never from a membership, which names no event (ADR 0670 §2); the operator
chose the manifests on the bus on 2026-11-11, and §14 already reads them.
module's name is written in its code; it is design 46 7-approval-mark §5's derivation, extended to events. *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.
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); 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 ADR 0286 §10 and
§11 make one: the address `mesh_wait`, the node, the module `node-tools`, its build, and every argument given
(`subjects`, `from`, `filter`, `timeout`), so the warrant binds the exact names, the filter and the bound. The tool
runner asks, judges and claims it as ADR 0286 §12, §13, §17 to §33 and §36 to §42 say of a marked tool, asking on
the seat `operator-channel` under its own module's name, `node-tools`. The ask's headline is "Wait on <names> on
<node>?" (shortened as ADR 0286 §15 says), and its explanation names every event name one per line, the filter,
and the bound as the timeout in minutes, then the act's fingerprint (ADR 0286 §16). On a warrant it accepts, it
subscribes, and the wait's timeout runs from then. One warrant is one wait (ADR 0286 §31): a watcher looping on a
marked event is approved call by call. Its answer carries ADR 0286 §34's line naming the approval, the run is
recorded as ADR 0286 §35 says, and the tool runner's `approvals` (ADR 0286 §25) lists the waits asked beside the
calls.
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
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
`operator-channel` in `uses` for it (that part's §7). The ask's headline is "Wait on <names> on <node>?"
(shortened as that part's §10 says), and its explanation names every event name one per line, the filter, and the
bound as the timeout in minutes, then the act's fingerprint. On a warrant it accepts, it subscribes, and the wait's
timeout runs from then. One warrant is one wait (that part's §15): a watcher looping on a marked event is approved
call by call. Its answer carries the line naming the approval and the warrant is recorded through
`mesh-hand-acts.warranted` (that part's §16), and `mesh_approvals` (that part's §18) lists the waits asked beside
the calls. *Why:* the operator, 2026-11-11: a marked wait runs only on the operator's warrant for that one wait,
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, ADR 0286
§37's or §38's words with "this wait" for "the call", only after its timeout or its caller's going, as §16
answers a refused subscription. Reason: ADR 0286 §14 answers within 5 seconds, which a watcher's loop would turn
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
@@ -345,5 +353,4 @@ milestone builds them first is a question open with the operator, and these line
`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). *Pending the record that extends
the approval mark.* §37's rows pass.
- **A marked wait runs only on the operator's warrant** (unit, and on the test bus): §37's rows pass.
+7 -5
View File
@@ -167,14 +167,16 @@ stream (ADR 0363 §2), the seat holders' events published there among them, a re
included. A restricted stream's grant value decides who consumes it, never who hears it, so the tool runner's hearing
needs no `consumes` and no entry in that value, and keeps no consumer of it.
*Pending the record that extends the approval mark (the operator, 2026-11-11):* **a marked event is heard only on
the operator's warrant.** The approval mark (ADR 0286) covers a module's events and streams as well as its tools and
**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
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. An
unmarked event is heard with no ask.
and says so. The bus grants the subjects, and the tool runner's own gate holds the mark, as the operator chose; the
gate is design 46's milestone 7, built in design 47's milestone G and delivered in one group with G and milestone
every-event, so the subjects and the gate reach the machines together. This departs from ADR 0286 §1 and §8 (tools and
verbs only, held by the bus) and from ADR 0671 §1 and §3 (every module stream heard with no exception) by the
operator's answer. An unmarked event is heard with no ask.
**What that makes audible, and what still bounds it.** Every session now hears the operator channel's `decided` of
every asker and the intake bench's events. So no event of a seat or a module carries a secret: no credential, token or
@@ -217,7 +219,7 @@ address gets a refused connection, which is the truthful answer.
| live: a watcher's `mesh_wait` on the status view's `delivered`, an event no manifest names, wakes on it with no rereading on a timer | ADR 0670 §2, milestone every-event |
| a composition test: the tool runner's runtime account is granted exactly `mesh.seat.*.event.>`, `mesh.mod.*.event.>` and `mesh.stream.*.event.>` beside what its carried modules declare, with no publish on another's events, no durable consumer for them and no wider subject; on a real bus it is refused a publish on an event subject, and another module's account a subscription on an event it does not consume | ADR 0670 §1, §3; ADR 0671 §1 |
| on a real bus, the tool runner's account hears a restricted stream's event with no entry in its grant value, still after the value changes, and is refused a durable consumer and an acknowledgement on it | ADR 0671 §1, §2 |
| *pending the record that extends the approval mark:* the tool runner's gate refuses a marked wait with no warrant and accepts one with a warrant bound to its exact names, filter and timeout; on the test bus a restricted stream's event is heard only after the warrant | the operator, 2026-11-11; ADR 0286 |
| the tool runner's gate refuses a marked wait with no warrant and accepts one with a warrant bound to its exact names, filter and timeout; on the test bus a restricted stream's event is heard only after the warrant | the operator, 2026-11-11; ADR 0286 |
| the manifest validator, as the checker runs it, refuses a manifest that carries `waits` as an unknown field, and accepts the tool runner's without it | ADR 0670 §2 |
| the milestone's prepare lists every event with its body's fields, and its code review confirms none carries a secret before the grant is delivered; every later change adding an event or a field is asked the same in its review | ADR 0670 §4 |
| nothing takes an operator's answer from a `decided` event's body; `claude_code_answer` is its one reader | ADR 0670 §5 |
@@ -0,0 +1,226 @@
---
layer: to-be
design: 46
milestone: 7-approval-mark
updated: 2026-11-11
code: [mesh-sdk, mesh-contracts, mesh-delivery, mesh-tools, mesh-catalog modules/zsh]
decisions:
- "ADR 0286"
- "ADR 0259"
- "ADR 0627"
- "ADR 0666"
- "ADR 0668"
- "ADR 0595"
- "ADR 0597"
- "ADR 0310"
- "ADR 0268"
---
# Design 46, milestone 7 — a call that needs the operator's approval
**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,
[§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").
**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).
**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.
## 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 (`<tool>`) or a seat verb it
serves (`<seat>.<verb>`), 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`.
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>/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.
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.
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.
## Where the tool runner reads it
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.<module>.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.
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.<module>` and `.cancel.<module>`, the hearing of its warrants on
`mesh.seat.operator-channel.event.decided.<module>` and the read of the router's record under `<module>.` 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.
## The act, and the first call
8. **The act is digested with operator-channel's client.** For a call of a marked tool or verb, the tool runner
builds an `operatorchannelclient.Act` with `address` (as called), `node` (the node that serves it), `module` and
`tool`, or `seat` and `verb`, `build` (the digest of the bundle it serves the module from), and `arg.<name>` for each
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
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.
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`
`operator`; `expires` 60 minutes after; no `on-expiry`; two options, `approve` (label Approve) and `decline` (label
Decline), both at level `approve`, each binding the act's digest; the headline "Run <tool> on <node>?",
"Run <tool>?" when that is longer than the router allows, ending "Part shown" whenever an argument is not shown
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.
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 <channel> at <time> (ask <id>, fingerprint …); make the same call again within 30 minutes of the
approval; the ask expires at <time>." *Why:* ADR 0286 §14, §37. A held call that runs on the Approve and returns
its outcome to the caller, which the operator asked for on 2026-10-16 (issue 417), is not this record's shape; it
is a question open with the operator. *How it is checked:* a row of §22's table holds the words.
## The runner's record, and the same call again
13. **The runner keeps its asks and claims in a state of its own, per machine.** The tool runner's manifest asks in
`requires` for a state `approvals`, template `state`, `per-machine`, `ttl-seconds` 172,800 (48 hours). Its keys:
`ask.<id>`, the asked call as the client keeps it (the ask, the act, the whole arguments, when asked, whether
acted on), and `act.<digest>`, the id of the newest ask for that act. The runner on each machine is the one writer
of its machine's bucket; a key is deleted by its time to live, never by hand. *Why:* ADR 0286 §24, §30 kept the
waiting call in the runner's record and claimed a warrant in the node's own file, kept 48 hours, by one writer per
node; the client built since keeps asks and marks a warrant acted on by compare-and-set in a module's bus state
(ADR 0259 §10, ADR 0627 §1), which is one writer per machine as a per-machine state (ADR 0595), so the file is not
built. *How it is checked:* the manifest test of `node-tools` holds the entry; §22's whole test reads the keys.
14. **The same call again is judged on the router's record.** When a call of a marked tool comes and `act.<digest>`
names an ask, the runner reads the router's own record of that ask under the module's name (`Client.Where`, the
seat state `asks`, key `<module>.<id>`), never a record of its own or the caller's; it composes the ask again from
the act and holds the warrant to it with `operatorchannelclient.For` (the same asker and ask, the ask's digest, a
person's choice, an option offered, at its level, before it expired); it requires the option to be `approve` and
the act to be the one it bound (`operatorchannelclient.Performs`: the same node, build and every argument); and it
requires the call within 30 minutes of the choice. It judges the newest ask it keeps for the act. *Why:* ADR 0286
§26 to §29, §32. *How it is checked:* rows of §22's table: approved, approved for other arguments, approved 31
minutes ago, declined, expired, another asker's warrant.
15. **One approval runs one call.** Before it runs, the runner marks `ask.<id>` acted on by compare-and-set
(`Client.Take`); a warrant marked before is refused, so one approval runs one call however often the call is made,
and a warrant replayed onto the bus runs nothing. After "already run", "expired" or "approved too long ago" the next
identical call asks anew, and the used mark stays. *Why:* ADR 0286 §30, §31, §33, §36. *How it is checked:* a row
of §22's table makes the approved call twice and runs it once; two calls at the same moment run it once.
16. **The call runs, and its warrant is recorded.** Once the checks pass, the runner runs the call and answers with
the tool's own answer and a line naming the approval ("run on the operator's approval via <channel> at <time>, ask
<id>"). It then calls `mesh-hand-acts.warranted` with `asker` (the module) and `ask` (the id) as a kept call (ADR
0668 §1, §2: the verb takes it once per ask), so a transport fault calls it again until it is answered, and one
unanswered after five minutes is the runner's `call-unanswered` finding. *Why:* ADR 0286 §34, §35; the hand-act log
is the seat `mesh-hand-acts`, whose `warranted` reads the router's own record, and its "again every ten minutes for
a week" is ADR 0668's kept call now. *How it is checked:* §22's whole test reads the hand-act recorded once.
17. **What a later call is told.** For the same call made again before it can run, the caller is told, as an error:
still waiting; declined (the same act is not asked again for 10 minutes, and the answer says so); expired,
approved too long ago or already run (each asks again, §15); or refused, with the router's words. An ask the router
holds back as the asker's fourth open one is still waiting, and `operator-channel.asks` lists it (ADR 0666 §12, as
G builds it). *Why:* ADR 0286 §38, §39; ADR 0286 §40's refusal of a fourth open ask was the router's before G, which
now offers a fourth and holds the oldest back. *How it is checked:* rows of §22's table.
18. **The tool `mesh_approvals` reads what was asked.** The mesh MCP server on each machine gains the tool
`mesh_approvals`: the calls asked on that machine in the last day, newest first, each with its state and
fingerprint, read from §13's state; given an ask's id, its whole arguments and the act's digest. It writes nothing.
*Why:* ADR 0286 §25 (the runner's `approvals`), named as the mesh MCP server's own tools are. *How it is checked:*
a row in the mesh MCP server's tool table test, and §22's whole test reads an asked call with it.
## Failing closed
19. **Nothing runs when a part of the check is missing.** A call of a marked tool is refused, runs nothing and says
why when: no module holds `operator-channel` (the seat list says no holder); the router refuses the ask; the ask
cannot be published; the router's record cannot be read; §13's state cannot be read or written, in which case no
marked call runs on that machine until it can; or the marks cannot be read (the manifests view or the seat list not
yet read, `mesh_first_views`). Each refusal is written in the runner's log with the ask's id and fingerprint, never
the arguments. *Why:* ADR 0286 §40, §42. *How it is checked:* one row of §22's table per case.
20. **The terminal is not exempt.** A marked tool called through `mesh-cli` or any caller asks as every call does: the
runner cannot tell who called. *Why:* ADR 0286 §41. *How it is checked:* the gate sits on the call's handler,
before the bundle, whatever the caller; the code review reads that no path to a bundle passes around it.
## Marked first
21. **`node-login-shell.execute` is marked in its seat's definition.** mesh-contracts' `seats/node-login-shell/seat.json`
marks the verb `execute` with `needs-approval`; the zsh module's manifest (mesh-catalog `modules/zsh`), which holds
the seat, gains `operator-channel` in `uses` and `approval-mark/1` in `capabilities`. The control-node keeps
withholding `execute`, and whether it serves it again under the mark stays the operator's setting. *Why:* ADR 0286
§44, §45: the reads it carried have their tools on every node (the seat `node-reads`, ADR 0310; issue 418 resolved
2026-10-12, answering on every node since 2026-10-16). No other tool or verb is marked in this milestone. *How it
is checked:* live, as the acceptance says.
## Tests and delivery
22. **The tests.** The tool runner's tests in `node-tools/internal/approval` are one table-driven test of the refusals
and answers (§9, §12, §14, §15, §17, §19) and one of the marks (§5), on the tool runner's test bus (`meshtest`),
the router's record and the warrant written there as the router writes them; and one whole test that starts the
runner against the real bus and store with a fixture marked module, makes a call, writes the operator's Approve as
the router's record, makes the call again, and reads the run, the hand-act and `mesh_approvals`. *Why:* the
operator's test budget (one whole test over many small ones). *How it is checked:* the check counts the module's
tests against its budget.
23. **One delivery group with milestone G and every-event.** The members are pushed and checked as one group with
design 47's milestone G and design 34's milestone every-event. The orders between them are the module graph's: the
indexer's manifest validator that reads `needs-approval` before any manifest or seat definition carrying it (a
reader edge), and the tool runner stating `approval-mark/1` before the zsh module's version asking for it is placed
(the capability hold, §6). *Why:* the operator, 2026-11-11, "Build the gate in G"; the operator, 2026-11-03, a
change is designed as it should be and its delivery is the delivery system's. *How it is checked:* the delivery
plan names both edges.
## Acceptance
- **A marked call is asked on the phone, does nothing, and runs once on the Approve** (live, read by tool call):
`node-login-shell.execute` on a node other than the control-node answers "Not run", the ask is on
`operator-channel.asks`, and after the operator's Approve the same call runs once, a third identical call is refused
as already run, and `mesh-hand-acts.hand-acts` names the warranted act (§10, §12, §14 to §16, §21).
- **The mark cannot be lost on the way** (unit): §5's and §8's tables pass; a bundle carries no mark.
- **It fails closed** (unit): §19's rows pass.
- **The validator refuses a mark nothing enforces** (unit): §4's rows pass.
@@ -1,9 +1,10 @@
---
layer: to-be
status: in-progress
code: [mesh-controller, mesh-tools, mesh-sdk, mesh-catalog]
updated: 2026-11-10
code: [mesh-controller, mesh-tools, mesh-sdk, mesh-catalog, mesh-contracts, mesh-delivery]
updated: 2026-11-11
decisions:
- "ADR 0668"
- "ADR 0667"
- "ADR 0666"
- "ADR 0347"
@@ -17,6 +18,11 @@ decisions:
- "ADR 0253"
- "ADR 0234"
- "ADR 0227"
- "ADR 0627"
- "ADR 0597"
- "ADR 0595"
- "ADR 0310"
- "ADR 0268"
---
# 46 — The conversation with the operator
@@ -561,44 +567,26 @@ said on the away channel, with its why.
### A call that needs the operator's approval
*Added 2026-10-10, ADR 0286.*
A module's manifest marks a tool, or a seat verb it serves, as **`needs-approval`**; a seat's definition may mark
a verb for every holder. The controller issues the mark in the runner's membership, never from the bundle, and
withholds a marked tool from a runner that does not declare `approval-mark/1`, by leaving its subject out of that
runner's bus permissions.
*Added 2026-10-10, ADR 0286; amended 2026-11-11 to the mesh as built and as design 47's milestone G leaves it.*
A module's manifest marks a tool, or a seat verb it serves, in **`needs-approval`**; a seat's definition may mark
a verb for every holder. The tool runner reads the marks from the manifests and the seat list on the bus, never from
the bundle, and a marked module runs only on a machine whose tool runner states that it honours the mark.
```
agent ──call──► tool runner (serving node) ──ask, under the module's name──► router ──► Telegram
agent ──call──► tool runner (serving node) ──ask, under the module's name──► seat operator-channel ──► Telegram
▲ │ "Not run: asked, ask <id>" │ Approve
│ ▼ ▼
└──same call──► runner reads the router's record ◄────────────── warrant (decided.<module>)
│ Warrant.For + Option.Performs on the act; within 30 min; claimed once
│ For + Performs on the act; within 30 min; claimed once in its own state
▼
runs the call once ──► mesh-controller.warranted (hand-act log)
runs the call once ──► seat mesh-hand-acts, warranted
```
- **The act** is the address, the node, the module and tool, the bundle's build and every argument given, digested
with the shared library's `ActDigest` (in Go and TypeScript, one set of vectors).
- **The first call asks and does nothing.** One ask per act (its `about` is the act's digest): "Run <tool> on
<node>?", the tool's first line, every argument whole where it may leave the mesh or in its shape with
‹path›, ‹address› or ‹withheld› and "Part shown" (whole when at most 2000 bytes and 40 lines, else the
first 20 lines and 1200 bytes), the fingerprint, and where the whole is read (the runner's
`approvals` on that node). Approve and Decline, both at approve; expiry 60 minutes; never a default. The call
answers as an error: "Not run", with the ask's id, that the call runs within 30 minutes of the approval, and
when the ask expires.
- **The same call again** runs once on Approve, read from the router's record, within 30 minutes of the choice,
claimed in the node's own file (kept 48 hours, one writer per node); the runner judges the newest ask it keeps
for the act, and after "already run", "expired" or "approved too long ago" the next identical call asks anew;
then `warranted` records the hand-act. Anything else is
refused and said: waiting, declined (not asked again for 10 minutes), expired, already run, or the router's
refusal.
- **Fail closed:** a secret's shape in an argument or more than 16 KiB of arguments, no operator channel, a
fourth open ask (until design 47's milestone G is delivered, after which a fourth is offered and the oldest held
back, §6), an unpublished ask, an unreadable router record, node file or mark: nothing runs.
- **The controller** enforces the same mark on its own verbs, asking as `mesh-controller`.
- **Marked first:** `node-login-shell.execute` on every node once the reads it carried have tools (issue 418).
`mesh-delivery.release` waits until a warrant the controller holds passes the mark of a verb it calls (issue
417), so a Release on a condition is not asked twice.
The first call asks and does nothing; the same call made again within 30 minutes of the operator's Approve runs once;
everything else is refused and said, and anything the check cannot read runs nothing. `node-login-shell.execute` is
marked first. Every line of it is in milestone 7's part,
[`46-milestones/7-approval-mark.md`](46-milestones/7-approval-mark.md), built and delivered with design 47's milestone
G (the operator, 2026-11-11).
### The hand-act
@@ -718,7 +706,7 @@ Each phase ends at its own *done when*. Owning repositories from [`repos.md`](..
| **5a — Asks on the bus** (ADR 0259) | the kinded bench, caller-named verbs, `proof` subjects and the records users read (controller); a bundle's seat traffic, compare-and-set state and its own account (tool runner); the asker's and channel's types (shared library); the router's asks, tokens, identities, judging, warrants, expiry and linking with its hour (messenger); `telegram` and `desk-channel`; the controller asking for its conditions' actions and acting on warrants | mesh-controller, mesh-tools, mesh-sdk, mesh-catalog | a held delivery released from the phone; the hand-act reads "the operator, via telegram, user id verified"; a desk Silence goes through an ask |
| **5 — Authorise, with TOTP** | `authorises` in the verb table; the router's checks for the code; direct calls refused, break-glass; `retire approve` takes `expect`; the hand-act fields; the TOTP seed as the router's state, enrolled at a terminal with ten recovery codes; `factor recover`, `factor status`, the local-only break-glass enrolment; no P2 tier before recovery codes exist; optional key enrolment; the self-check probe for the away channel; agents' grants lose the authorising verbs | mesh-controller; mesh-catalog (agents' grants, the desk's code prompt) | one controller test per refusal passes; at the desk, approve needs a code; a recovery drill re-enrols with a recovery code, and is announced |
| **6 — Telegram live** | the `telegram` module (built in 5a) with replies and forced-reply codes, under its own account on the control node; the operator's two bots configured; the watcher assigned off the control node; the dead-man ping | mesh-catalog | the self-check says the away channel carries every tier; rehearsals: an approve and a destroy answered on the phone, with the hand-acts read |
| **7 — A call that needs approval** (ADR 0286) | `needs-approval` in the manifest and the seat list, issued in the membership, withheld from a runner without `approval-mark/1` in its bus permissions (controller); the act's digest in TypeScript beside Go, with shared vectors (shared library); the check, the ask, the runner's `approvals`, the node's used file and `warranted` (tool runner); the controller's own verbs; `execute` marked (the controller's seat list) once issue 418's reads exist | mesh-controller, mesh-sdk, mesh-tools, mesh-catalog | a marked call on a node is asked on the phone, does nothing, runs once when made again after Approve, is refused the third time and asked again, and `hand-acts` shows the warrant |
| **7 — A call that needs approval** (ADR 0286), delivered with design 47's milestone G | the mark in the manifest and the seat definition, read by the tool runner from the bus; the manifest validator's refusals; the capability `approval-mark/1`; the act, the ask, the check, the claim in the runner's own state, `warranted` and `mesh_approvals`; `execute` marked. Its part is [`46-milestones/7-approval-mark.md`](46-milestones/7-approval-mark.md) | mesh-sdk, mesh-contracts, mesh-delivery, mesh-tools, mesh-catalog | its part's acceptance |
| **8 — No ask hidden** (ADR 0666 §12), delivered with design 47's milestone G | a fourth ask offered and the asker's oldest held back, offered again when one ends; `asks` lists the held-back asks; the router's tool `ask-again`; the pipeline runner's `approval` asks and their `about` | mesh-catalog (the router) | a fourth ask and a declined approval each reach the operator; `asks` shows the held-back one |
**Switching phase 5a on: the operator's steps, in order** (amended 2026-10-09, ADR 0259). Each line is typed with