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:
+5
-4
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user