Merge pull request 'The approval mark and every-event: the prepare's answers, so both parts are complete for develop with milestone G' (#542) from design/34-wait-on-any-event into main

This commit was merged in pull request #542.
This commit is contained in:
2026-11-11 08:56:23 +00:00
2 changed files with 154 additions and 78 deletions
@@ -65,7 +65,8 @@ that holds its mark reach the machines together.
`EventSubjects` answers for its cases is covered by a subject of `EveryEvent`. `EventSubjects` answers for its cases is covered by a subject of `EveryEvent`.
2. **The manifest loses `waits`.** `catalogue.Manifest.Waits` and its comment go from `go/catalogue/manifest.go`; 2. **The manifest loses `waits`.** `catalogue.Manifest.Waits` and its comment go from `go/catalogue/manifest.go`;
the comment on `ToolRunner` loses "and the only module that may declare `waits`". `go/catalogue/waits_test.go` is the comment on `ToolRunner` loses "and the only module that may declare `waits`". `go/catalogue/waits_test.go` is
deleted, and one row is added to the decoder's existing table of unknown fields: a manifest deleted, and one row is added to the nearest table over raw manifests, `go/catalogue/invokes_test.go`'s
`TestAManifestMaySayWhatItInvokes` (Q-T): a manifest
`{"module":"node-tools","version":"1","tool-runner":true,"waits":["a.b"]}` decodes with `UnknownField()` naming `{"module":"node-tools","version":"1","tool-runner":true,"waits":["a.b"]}` decodes with `UnknownField()` naming
`waits` (ADR 0670 §2). The decoder itself is unchanged: a stored manifest that still carries `waits` is read `waits` (ADR 0670 §2). The decoder itself is unchanged: a stored manifest that still carries `waits` is read
without it (ADR 0578 §3). without it (ADR 0578 §3).
@@ -123,7 +124,8 @@ that holds its mark reach the machines together.
11. **Its manifest's changes, every one.** `node-tools/module.json` drops the key `waits` and its five entries (ADR 11. **Its manifest's changes, every one.** `node-tools/module.json` drops the key `waits` and its five entries (ADR
0670 §2); gains `operator-channel` in `uses`, for the asks of marked waits (§35; design 46 7-approval-mark §7); gains 0670 §2); gains `operator-channel` in `uses`, for the asks of marked waits (§35; design 46 7-approval-mark §7); gains
in `requires` the state `approvals` of design 46 7-approval-mark §13; and gains the stream `node-tools-findings` and in its old `state` section the per-machine state `approvals` of design 46 7-approval-mark §13 (the tool runner is a
foundation part, ADR 0619 §2, §3); and gains the stream `node-tools-findings` and
the `consumes` of `mesh-conditions.findings-asked` that design 47 G §387 writes. Its `version` stays `2`, a label the `consumes` of `mesh-conditions.findings-asked` that design 47 G §387 writes. Its `version` stays `2`, a label
no reader judges. Nothing else in it changes. no reader judges. Nothing else in it changes.
12. **The publication time is kept.** `bus.Heard` gains `Published`, read from the event's header `x-time`, which 12. **The publication time is kept.** `bus.Heard` gains `Published`, read from the event's header `x-time`, which
@@ -182,8 +184,9 @@ that holds its mark reach the machines together.
answered after the timeout with §16's words; `TestMeshWaitWithNoFamiliesIssuedWaitsOnNothing` is deleted. In answered after the timeout with §16's words; `TestMeshWaitWithNoFamiliesIssuedWaitsOnNothing` is deleted. In
`bus/wait_test.go`, `TestTheBusRefusesAWaitOutsideTheGrant` stays as §16's refusal test, with a timeout of a `bus/wait_test.go`, `TestTheBusRefusesAWaitOutsideTheGrant` stays as §16's refusal test, with a timeout of a
fraction of a second, its comment citing ADR 0670 §1. The manifest test also holds each change of §11: no `waits`, fraction of a second, its comment citing ADR 0670 §1. The manifest test also holds each change of §11: no `waits`,
`operator-channel` in `uses`, the state `approvals` in `requires` with its options, and the finding stream. No test `operator-channel` in `uses`, the state `approvals` in the old `state` section, per machine with its time to live,
function is added: the module stays within its budget. and none in `requires`, and the finding stream. New
test functions are allowed within the module's budget of 500 unit and 50 integration tests (Q-TB).
## The claude-code module's Stop hook ## The claude-code module's Stop hook
@@ -316,7 +319,8 @@ approves, for a marked event or a restricted stream.
`from: seat` is never marked. `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 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` and the tool `mesh_wait`, 7-approval-mark §8 makes one: the address `mesh_wait`, the node, the module `node-tools` and the tool `mesh_wait`,
its build (the digest of the tool runner's own artifact), and every its build (the tool runner's own fingerprint, as the node-engine's `running.<node>.node-tools` last said it, which the
tool runner hears, design 47 G §309; Q-BD; while it is unknown, a marked wait is refused, saying so), and every
argument given (`subjects`, `from`, `filter`, `timeout`), so the warrant binds the exact names, the filter and the 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 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 marked tool, asking on the seat `operator-channel` under its own module's name, `node-tools`, whose manifest gains
@@ -348,7 +352,8 @@ approves, for a marked event or a restricted stream.
so the restricted stream's event is heard only after the warrant; with a warrant for other names, another filter so the restricted stream's event is heard only after the warrant; with a warrant for other names, another filter
or another timeout it is refused; the same names `from: seat` ask nothing; a wait named `<module>.<event>.<node>` or another timeout it is refused; the same names `from: seat` ask nothing; a wait named `<module>.<event>.<node>`
under a marked `<module>.<event>` is marked; and before the catalog half is read, a marked wait asks nothing, is under a marked `<module>.<event>` is marked; and before the catalog half is read, a marked wait asks nothing, is
judged when the first view comes within its timeout, and is refused at its timeout when it does not. The warrant is judged when the first view comes within its timeout, and is refused at its timeout when it does not. A marked wait while
the tool runner's own fingerprint is unknown is refused, saying so. The warrant is
written on the test bus as the router's record. `Overlaps` gains rows in §1's table (`mesh.stream.mail.event.mail.>` overlaps written on the test bus as the router's record. `Overlaps` gains rows in §1's table (`mesh.stream.mail.event.mail.>` overlaps
`mesh.stream.mail.event.mail.*.message`; `mesh.mod.mail.event.x` does not overlap `mesh.stream.mail.event.x`), and `mesh.stream.mail.event.mail.*.message`; `mesh.mod.mail.event.x` does not overlap `mesh.stream.mail.event.x`), and
§32's refusal a row in the indexer's `judge/rules_test.go`. §32's refusal a row in the indexer's `judge/rules_test.go`.
@@ -3,7 +3,7 @@ layer: to-be
design: 46 design: 46
milestone: 7-approval-mark milestone: 7-approval-mark
updated: 2026-11-11 updated: 2026-11-11
code: [mesh-sdk, mesh-contracts, mesh-delivery, mesh-tools, mesh-catalog modules/zsh, mesh-catalog modules/gitea] code: [mesh-sdk, mesh-contracts, mesh-delivery, mesh-tools, mesh-host, mesh-catalog modules/zsh, mesh-catalog modules/gitea, mesh-catalog modules/mesh-files]
decisions: decisions:
- "ADR 0286" - "ADR 0286"
- "ADR 0259" - "ADR 0259"
@@ -14,6 +14,8 @@ decisions:
- "ADR 0595" - "ADR 0595"
- "ADR 0597" - "ADR 0597"
- "ADR 0310" - "ADR 0310"
- "ADR 0394"
- "ADR 0619"
- "ADR 0268" - "ADR 0268"
- "ADR 0564" - "ADR 0564"
--- ---
@@ -50,26 +52,36 @@ and not a retry on a timer (§16), and a fourth open ask is offered as G makes i
not hold for a process that reads the tool runner's bus credential (the file `broker` in its state directory, not hold for a process that reads the tool runner's bus credential (the file `broker` in its state directory,
`own-secrets`) and speaks to the bus itself: that account may subscribe a marked subject, a restricted stream's among `own-secrets`) and speaks to the bus itself: that account may subscribe a marked subject, a restricted stream's among
them, on a machine whose runner honours the mark. Read on the workstation on 2026-11-11 with `node-reads.stat`, the them, on a machine whose runner honours the mark. Read on the workstation on 2026-11-11 with `node-reads.stat`, the
file is mode 0600, owned by the operator's account, in a directory of mode 0755: every process running as the file is mode 0600, owned by the operator's account, in a directory of mode 0755, and the prepare of 2026-11-11 read
the same mode and owner on all four nodes: every process running as the
operator's account, every module on the machine's shared account and every agent session among them, can read it and operator's account, every module on the machine's shared account and every agent session among them, can read it and
hear a restricted stream without a warrant. This is the same known limit as design 47 G §385 (modules running as the hear a restricted stream without a warrant. This is the same known limit as design 47 G §385 (modules running as the
machine's account, issue 1966 note 193, whose fix carries its check), and this milestone does not change it: that a machine's account, issue 1966 note 193, whose fix carries its check), and this milestone does not change it: that a
restricted stream is heard only on a warrant holds against every caller once that fix runs modules and sessions on restricted stream is heard only on a warrant holds against every caller once that fix runs modules and sessions on
accounts that cannot read the file, and until then against callers that go through the tool runner. The accounts that cannot read the file, and until then against callers that go through the tool runner. The
prepare reads the file's mode and owner on every node with `<node>/node-reads.stat` and writes them in the milestone prepare reads the file's mode and owner on every node with `<node>/node-files.stat` and writes them in the milestone
file; a node where the file is readable beyond the operator's account is a finding of the prepare. file; a node where the file is readable beyond the operator's account is a finding of the prepare. The forge module's
own credential (§21) lies in a directory of mode 0700, owned by the forge's account, which the reading account cannot
enter; `node-files.stat` answers such a file's mode from this milestone (§21a), and the prepare reads it then.
## The mark ## The mark
1. **A module marks its tools and verbs in `needs-approval`.** mesh-sdk's `catalogue.Manifest` gains `NeedsApproval`, 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 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 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:* unit, a row in the 0286 §1, §49 (each marking is a manifest change, reviewed like any other). Only the contract's manifest reads
decoder's table test in `go/catalogue`; the manifest reads baseline gains `word:needs-approval`. (`ManifestReads`) gain `word:needs-approval`; the baseline of the engines before any stated what it reads
(`manifest_reads_baseline.txt`) is unchanged, since none of them reads the word (Q-BL). *How it is checked:* unit, a
row in `go/catalogue/invokes_test.go`'s `TestAManifestMaySayWhatItInvokes`, the nearest table over raw manifests
(Q-T): a manifest naming a tool, a seat verb and an event in `needs-approval` decodes all three with `UnknownField()`
empty, and one without it has no mark.
2. **A seat's definition marks a verb for every holder.** mesh-sdk's `seatdef` reads a verb's `needs-approval`, a 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); the seat definitions the catalog half boolean, in a seat definition (`seats/<seat>/seat.json` in mesh-contracts); the seat definitions the catalog half
carries (design 47 G §194) carry it on the verb. *Why:* ADR 0286 §2. *How it is checked:* unit, a fixture row in carries (design 47 G §194) carry it on the verb. The definition format's own schema in mesh-contracts
`seatdef`'s table test. (`format/seat-definition.schema.json`) gains a verb's `needs-approval`, a boolean (Q-SC). Every strict reader of a
seat definition reads the word from G's reader release (design 47 G §340), so no reader refuses a definition that
carries it (Q-RR). *Why:* ADR 0286 §2. *How it is checked:* unit, a fixture row in `go/seatdef/seatdef_test.go`'s
`TestADefinitionIsReadStrictlyNamingWhatIsWrong` (Q-T), and the schema validates the login shell's definition of §21.
3. **Marks add up.** A verb is marked when its seat's definition or its holder's `needs-approval` marks it; a holder 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:* unit, a row of §5's table. cannot unmark what its seat marks. *Why:* ADR 0286 §3. *How it is checked:* unit, a row of §5's table.
4. **The manifest validator refuses a mark it cannot honour.** The indexer's manifest validator (`judge/rules.go`) 4. **The manifest validator refuses a mark it cannot honour.** The indexer's manifest validator (`judge/rules.go`)
@@ -89,50 +101,71 @@ file; a node where the file is readable beyond the operator's account is a findi
enforce its own verbs, and no other own-process server has a gate, so such a mark is refused rather than ignored; enforce its own verbs, and no other own-process server has a gate, so such a mark is refused rather than ignored;
and an entry that names two things is read one way by one reader and another way by the next (the design's choices and an entry that names two things is read one way by one reader and another way by the next (the design's choices
under ADR 0286 §4, §40, the refusal kept by lead-47 on 2026-11-11). *How it is checked:* unit, one row per refusal under ADR 0286 §4, §40, the refusal kept by lead-47 on 2026-11-11). *How it is checked:* unit, one row per refusal
in `judge/rules_test.go`, rows that accept the zsh and forge manifests of §21, and a row of §22's mark table for in `judge/rules_test.go`'s `TestRulesManifests` (Q-T), rows that accept the zsh and forge manifests of §21, and a row of §22's mark table for
`mark-unserved`. `mark-unserved`.
## Where the marks are read, and where they are withheld ## Where the marks are read, and where they are withheld
5. **From the catalog half on the bus, never from a bundle.** The indexer's catalog graph entry of a module (design 47 5. **From the catalog half on the bus, never from a bundle.** The indexer's catalog graph entry of a module (design 47
G §174) gains its `needs-approval`, its `emits`, and for each stream it declares its events and whether it carries a G §174) carries the validated manifest whole (design 47 G §443), its `needs-approval`, its `emits` and its streams
`grant`; no part but the indexer reads a manifest (the operator, 2026-11-11: "only the indexer uses the manifest to (their events as the manifest spells them, and whether each carries a `grant`) among it; mesh-sdk's `buscompose`
build the module catalog"); the seat exports `WirePattern`, which a reader uses to turn a stream's events into the bus's subjects, the grants part's own
copy going (Q-WP). No part but the indexer reads a manifest: every other part reads the entry of the build concerned
from the catalog half (design 47 G §442; the operator, 2026-11-11: "only the indexer uses the manifest to build the
module catalog"; Q-MR), the grants part's `declaredFor` among them for every grant, not only the marks; the seat
definitions of the catalog half carry the verb marks (§2). The tool runner, which from G takes its first view of the definitions of the catalog half carry the verb marks (§2). The tool runner, which from G takes its first view of the
catalog half by `mesh-catalog.revision` and follows `catalog-revision` (design 47 G §196), derives from it, for each catalog half by `mesh-catalog.revision` and follows `catalog-revision` (design 47 G §196), derives from it, for each
module it serves, its marked tools and verbs, in a package of its own, `node-tools/internal/approval`, again on module it serves, its marked tools and verbs, in a package of its own, `node-tools/internal/approval`, again on
every revision it hears. No module's name is written in its code. The marks follow the running version: the runner every revision it hears. No module's name is written in its code. The marks follow the running version: a bundle's
applies the marks of the module's version it serves, and, while the newest revision's entry for that module served version is the fingerprint its composed environment carries (design 47 G §424), whose entry the runner reads
differs, the union of both, so a mark is never lost before or after the module moves. A mark added while a call by `mesh-catalog.entry` (design 47 G §443) when it loads the bundle; it applies that entry's marks and, while the
waits applies from the next call, which the answer of §12 says. *Why:* ADR 0286 §5 had the controller issue the mark newest revision's entry for that module differs, the union of both, so a mark is never lost before or after the
in a membership so that a bundle could not drop it; no controller issues memberships now, and the judged catalog module moves, and a delivery that drops a mark while the old bundle still runs is still asked. When the served
half is the same guarantee: the bundle never carries the mark (ADR 0656 §6, one source for which modules exist). entry cannot be read, every marked call on that runtime is refused (§19). A mark added while a call waits applies
The union keeps ADR 0286 §3's "marks add up" across a version change. *How it is checked:* unit, a table test in from the next call, which the answer of §12 says. *Why:* ADR 0286 §5 had the controller issue the mark in a
`approval` derives the marks from fixture catalog halves, §3's row, a version change and the union among them. membership so that a bundle could not drop it; no controller issues memberships now, and the judged catalog half is
the same guarantee: the bundle never carries the mark (ADR 0656 §6, one source for which modules exist). The union
keeps ADR 0286 §3's "marks add up" across a version change. The served version is read from what the bundle runs,
not from a placement, which can change before the bundle does (step 0b). *How it is checked:* unit, a table test in
`approval` derives the marks from fixture catalog halves and served entries, §3's row, a version change and the
union among them, and a served entry that cannot be read.
6. **A machine whose tool runner does not honour the mark serves no marked tool or verb.** The tool runner states 6. **A machine whose tool runner does not honour the mark serves no marked tool or verb.** The tool runner states
`approval-mark/1` among its call capabilities (`runtime.CallCapabilities`), which its announcement carries and its `approval-mark/1` among its call capabilities (`runtime.CallCapabilities`), which its announcement carries with
node's node-engine takes into the node's record; the assignment half carries it among the capability readings the tool runner's own fingerprint, and its node's node-engine takes both into the node's record; the assignment
(design 47 G §194). The grants part, composing a runtime's account, reads that runtime's node's readings: where the half carries, for each node, its capability readings, each with the fingerprint of the runner that announced it
tool runner does not state `approval-mark/1`, where the node has no readings, or where its readings are older than (design 47 G §194; Q-CR). The grants part, composing a runtime's account, reads that runtime's node's readings and
the tool runner's version on that node (failing closed as §19), it composes a subscribe deny of every marked the fingerprint the node-engine's `running.<node>.node-tools` last said running (design 47 G §309): where the tool
subject of every module the runtime carries, the marks read from the catalog half as §5 has them: runner does not state `approval-mark/1`, where the node has no readings, or where the readings' fingerprint differs
`mesh.mod.<module>.tool.<tool>` and the same followed by `.*`; the from the running one or is absent (an older reading, failing closed as §19; no time is compared), it composes a
caller-named subject mesh-sdk's `process.Call` sends on first, `mesh.mod.<module>.call.<tool>.>`; and for a marked subscribe deny of every marked subject of every module the runtime carries, the marks read from the catalog half
verb `mesh.seat.<seat>.tool.<verb>`, the same followed by `.<node>`, and `mesh.seat.<seat>.call.<verb>.<node>.>`. as §5 has them: `mesh.mod.<module>.tool.<tool>` and the same followed by `.*`; the caller-named subject mesh-sdk's
The bus's deny outranks its allow of `mesh.mod.<module>.tool.>` for each. The tool runner's version on a node is read from the `process.Call` sends on first, `mesh.mod.<module>.call.<tool>.>`; for a marked verb of a seat held per node
same assignment half revision, its entry for `node-tools` on that node (the module version placed there, design 47 `mesh.seat.<seat>.tool.<verb>`, the same followed by `.<node>`, and `mesh.seat.<seat>.call.<verb>.<node>.>`; and for
G §175), against the version the node-engine's `running.<node>.node-tools` last said running (design 47 G §309); a marked verb of a seat held once for the mesh `mesh.seat.<seat>.tool.<verb>` and `mesh.seat.<seat>.call.<verb>.>`,
readings that revision names from before that version ran are older. Going back to an older tool runner on a since its subjects name no node (Q-DL). The bus's deny outranks its allow of `mesh.mod.<module>.tool.>` for each.
node leaves the window until its readings update, in which the old deny or none stands; that window is accepted. `Permissions` gains `SubscribeDeny`, written into the user list as the server's Going back to an older tool runner on a node leaves the window until its readings update, in which the old deny or
subscribe deny, as `PublishDeny` is for publishing. The withheld tools and verbs are named in the grants part's read none stands; that window is accepted. `Permissions` gains `SubscribeDeny`, written into the user list as the
of that account (`mesh-grants.grants_user_list`, a field `withheld` with each subject and "its tool runner does not server's subscribe deny, as `PublishDeny` is for publishing; the user list's `User` gains `subscribe_deny` beside
state approval-mark/1"), and in the node-engine's `composition` of the module. *Why:* ADR 0286 §7, §8, §9: the bus `publish_deny`, in its comparison and in the differences the grants part says (Q-SD). An added `subscribe_deny` is a
narrowing: the user-list guard lets it pass at once, on the first composition after the release, and counts it as
no right lost. The withheld tools and verbs are named in the grants part's read of that account
(`mesh-grants.grants_user_list`, a field `withheld` with each subject and "its tool runner does not state
approval-mark/1"), and in the node-engine's `composition` of the module, which reads them through a verb `withheld`
that the seat `mesh-grants`'s definition gains: class `read`, callers the node-engine, persons and the operator,
input `node`, answer each module's withheld subjects and why, with a fixture in the definition; the node-engine's
manifest gains `mesh-grants` in `uses`; when the verb does not answer, the composition says "withheld unknown: the
grants part does not answer"; never read from the grants part's state (Q-NE). *Why:* ADR 0286 §7, §8, §9: the bus
enforces the mark; the runner's status of ADR 0286 §7 is read from the grants part's account read, since a runner enforces the mark; the runner's status of ADR 0286 §7 is read from the grants part's account read, since a runner
that does not honour the mark is a version that cannot say so. A call that reaches no responder because its that does not honour the mark is a version that cannot say so. A reading is tied to the runner that made it, not to
a time, so a clock or a late reading cannot lift the deny (step 0b). A call that reaches no responder because its
subject is withheld is answered by the mesh MCP server naming `withheld` and the grants part's read. *How it is subject is withheld is answered by the mesh MCP server naming `withheld` and the grants part's read. *How it is
checked:* unit, the grants part's composition table: readings lacking the capability, no readings, and readings checked:* unit, the grants part's composition table: readings lacking the capability, no readings, readings with
older than the runner each compose the deny of every subject above, the caller-named ones and a marked seat verb's another runner's fingerprint and readings with none each compose the deny of every subject above, the caller-named
`.<node>` form among them, each named in `withheld`; readings with the capability compose no deny; integration, the ones, a marked seat verb's `.<node>` form and a mesh-wide seat's marked verb among them, each named in `withheld`;
readings with the capability and the running fingerprint compose no deny; the release's first composition passes
the user-list guard with the deny added; a node gaining `approval-mark/1`, where only its readings change, has the
deny removed; unit, the assigner's table: a reading keeps the announcing runner's fingerprint; whole (node-engine),
`composition` shows the withheld subjects, and with the grants part down says "withheld unknown"; integration, the
grants part's real-bus test: an account composed with the deny is refused delivery of a call on a marked subject grants part's real-bus test: an account composed with the deny is refused delivery of a call on a marked subject
while it holds `mesh.mod.<module>.tool.>`, and so is a call sent through `process.Call` on the caller-named while it holds `mesh.mod.<module>.tool.>`, and so is a call sent through `process.Call` on the caller-named
subject. subject.
@@ -148,7 +181,7 @@ file; a node where the file is readable beyond the operator's account is a findi
8. **The act is digested with operator-channel's client.** For a call of a marked tool or verb, the tool runner 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 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 `tool`, or `seat` and `verb`, `build` (the fingerprint the bundle's environment carries, the version it serves, §5; Q-BD), 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 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 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 mesh-contracts since ADR 0627 §1. The TypeScript twin ADR 0286 named is not built: the tool runner is Go, and no
@@ -164,9 +197,11 @@ file; a node where the file is readable beyond the operator's account is a findi
every asker today), is `<node>.`; never before the router's record it reads through `Record` and `Where`, whose key every asker today), is `<node>.`; never before the router's record it reads through `Record` and `Where`, whose key
stays `<module>.<id>` in the seat state `asks`; unless an ask for the same act is stays `<module>.<id>` in the seat state `asks`; 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` 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 `operator`; `expires` 60 minutes after; `on-expiry` "nothing runs; call again to ask anew", since the client's
`Check` refuses an authorising ask without it, and no option is taken on expiry (ADR 0286 §22; Q-OC2); 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>?", 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 "Run <tool>?" when that is longer than the router allows (`HeadlineLength`, 60), and, when that too is longer,
the names cut with "…" to fit (Q-HL), 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 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 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 fingerprint, its digest's first 24 hexadecimal digits in groups of four. *Why:* ADR 0286 §13, §15, §16, §20 to
@@ -188,22 +223,29 @@ file; a node where the file is readable beyond the operator's account is a findi
called is named in the answer ("marked since <revision>"). *Why:* ADR 0286 §14, §37; the caller calls again, as called is named in the answer ("marked since <revision>"). *Why:* ADR 0286 §14, §37; the caller calls again, as
the operator chose on 2026-11-11 ("Call again (as designed)") over a held call that runs on the Approve and returns the operator chose on 2026-11-11 ("Call again (as designed)") over a held call that runs on the Approve and returns
its outcome (issue 417, 2026-10-16). *How it is checked:* unit, its outcome (issue 417, 2026-10-16). *How it is checked:* unit,
rows of §22's table hold both answers; a row with no router holds that a second call asks nothing new. rows of §22's table hold both answers; a row with no router holds that a second call asks nothing new; a row holds
a headline whose names are cut with "…" to 60 characters (Q-HL).
## The runner's record, and the same call again ## 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 13. **The runner keeps its asks and claims in a state of its own, per machine.** The tool runner, a foundation part,
`requires` for a state `approvals`, template `state`, `per-machine`, `ttl-seconds` 172,800 (48 hours), cap 64 MiB. declares a state `approvals` in its manifest's old `state` section, per machine, with a time to live of 172,800
seconds (48 hours), which the grants part creates as it creates a foundation part's states (ADR 0619 §2, §3; the bus
module refuses a per-machine state in the new form today). Its size is the old form's fixed one, 64 MiB
(`StateMaxBytes`), which §18 reads from the bucket, and a value is at most 256 KiB: an ask whose kept call would pass
it fails closed, naming the cause (ADR 0595 §3; Q-CAP). A runner whose membership binds no `approvals` serves every
unmarked call as before and refuses only marked ones (§19).
Its keys: `<node>.ask.<id>`, the asked call as the client keeps it (the ask, the act, the whole arguments, when Its keys: `<node>.ask.<id>`, the asked call as the client keeps it (the ask, the act, the whole arguments, when
asked, whether acted on, and `recorded` once `warranted` answered, §16), and `<node>.act.<digest>`, the id of the asked, whether acted on, and `recorded` once `warranted` answered, §16; the client's `Kept` gains `act`, `arguments`
and `recorded` for it, Q-KP), and `<node>.act.<digest>`, the id of the
newest ask for that act. The runner on each machine is the one writer of its machine's keys; a key is deleted by newest ask for that act. The runner on each machine is the one writer of its machine's keys; a key is deleted by
its time to live, never by hand. A per-machine state reaches today only the key named for its machine; mesh-sdk's its time to live, never by hand. A per-machine state reaches today only the key named for its machine; mesh-sdk's
per-machine form and the grants part's `stateAccess` gain the keys below it, `<node>.>`, for every per-machine per-machine form and the grants part's `stateAccess` gain the keys below it, `<node>.>`, for every per-machine
state, a bundle's membership (`stateIssuedFor`, ADR 0260 §18) too, in the same form: one more subject per state, a bundle's membership (`stateIssuedFor`, ADR 0260 §18) too, in the same form: one more subject per
per-machine state on each account, and every module's per-machine keys keep their form; mesh-sdk's rebuild of it per-machine state on each account, and every module's per-machine keys keep their form; mesh-sdk's rebuild of it
rides in G's group. The cap is the bucket's, shared by every machine's keys. A write the bus refuses (the cap rides in G's group. That size is the bucket's, shared by every machine's keys. A write the bus refuses (the size
reached, or any other) fails closed under §19 and is said in the answer; `mesh_approvals` reports the bucket's fill reached, or any other) fails closed under §19 and is said in the answer; `mesh_approvals` reports the bucket's fill
against its cap, saying that the fill and the cap are every machine's together. A run still unrecorded (§16) an hour before its key's 48 hours end is written in the runner's log against its size read from the bucket, saying that both are every machine's together. A run still unrecorded (§16) an hour before its key's 48 hours end is written in the runner's log
as an error naming the ask; this is written only while the runner runs, so a machine asleep through that hour keeps as an error naming the ask; this is written only while the runner runs, so a machine asleep through that hour keeps
only the `call-unanswered` history of §16, which is accepted. only the `call-unanswered` history of §16, which is accepted.
*Why:* ADR 0286 §24, §30 kept the waiting call in the runner's record and claimed a warrant in the node's own file, *Why:* ADR 0286 §24, §30 kept the waiting call in the runner's record and claimed a warrant in the node's own file,
@@ -211,12 +253,15 @@ file; a node where the file is readable beyond the operator's account is a findi
compare-and-set in a module's bus state (ADR 0259 §10, ADR 0627 §1), and a per-machine state (ADR 0595) is one compare-and-set in a module's bus state (ADR 0259 §10, ADR 0627 §1), and a per-machine state (ADR 0595) is one
writer per machine, so the file is not built. The keys below the machine's own depart from ADR 0260 §16 (a writer per machine, so the file is not built. The keys below the machine's own depart from ADR 0260 §16 (a
per-machine state reaches only its machine's key), the design's choice following the operator's answers of per-machine state reaches only its machine's key), the design's choice following the operator's answers of
2026-11-11 (the gate built in G); the cap and the key form are the design's choices too. *How it is 2026-11-11 (the gate built in G); the key form is the design's choice too. *How it is
checked:* unit, the manifest test of `node-tools` holds the entry; the grants part's composition table holds the checked:* unit, the manifest test of `node-tools` holds the old-form entry (per machine, its time to live) and no
`approvals` in `requires`; a row of §22's table: an ask over 256 KiB refused naming the cause; another: with no
binding, an unmarked call runs and a marked one is refused; the grants part's composition table holds the
`<node>.>` reach and no other node's; integration, §22's whole test reads the keys. `<node>.>` reach and no other node's; integration, §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 `<node>.act.` 14. **The same call again is judged on the router's record.** When a call of a marked tool comes and `<node>.act.`
names an ask, the runner reads the router's own record of that ask under the module's name (`Client.Where`, the 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 seat state `asks`, key `<module>.<id>`, whose value in operator-channel's definition gains the `warrant` it already
carries, so the definition names what `Where` reads; Q-OC1), 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 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 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 the act to be the one it bound (`operatorchannelclient.Performs`: the same node, build and every argument); and it
@@ -231,15 +276,17 @@ file; a node where the file is readable beyond the operator's account is a findi
16. **The call runs, and its warrant is recorded, across a restart too.** Once the checks pass, the runner runs the 16. **The call runs, and its warrant is recorded, across a restart too.** 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 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 <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), and on its answer writes `recorded` on id) as a kept call in mesh-sdk's kept-call form (ADR 0668 §1, §2, §7; design 47 G §351, §425: the verb takes it once
`<node>.ask.<id>`. At each start the runner calls `warranted` again for every acted, unrecorded ask its keys hold, per ask), the key that keeps it open being `<node>.ask.<id>` without `recorded`, and no event replacing it; it is not
`busretry`, which stops after 30 seconds (Q-KC). On its answer the runner writes `recorded` on `<node>.ask.<id>`. At each start the runner calls `warranted` again for every acted, unrecorded ask its keys hold,
within their 48 hours, each named in its `findings-stated`'s `pending` until it is made (design 47 G §384). One within their 48 hours, each named in its `findings-stated`'s `pending` until it is made (design 47 G §384). One
unanswered after five minutes is the runner's finding `<node>.<ask id>.call-unanswered` in its state `findings`, unanswered after five minutes is the runner's finding `<node>.<ask id>.call-unanswered` in its state `findings`,
said on its stream `node-tools-findings` (design 47 G §383, §387), which the condition keeper hears; it clears when said on its stream `node-tools-findings` (design 47 G §383, §387), which the condition keeper hears; it clears when
`warranted` answers. *Why:* ADR 0286 §34, §35; the hand-act log is the seat `mesh-hand-acts`, whose `warranted` `warranted` answers. *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, kept reads the router's own record, and its "again every ten minutes for a week" is ADR 0668's kept call now, kept
across a restart by the `recorded` mark (ADR 0668 §3, ADR 0564). *How it is checked:* integration, §22's whole test across a restart by the `recorded` mark (ADR 0668 §3, ADR 0564). *How it is checked:* integration, §22's whole test
reads the hand-act recorded once, also when the runner is restarted after the run and before `warranted` answers. reads the hand-act recorded once, also when the runner is restarted after the run and before `warranted` answers;
unit, §18's row of `record`.
17. **What a later call is told.** For the same call made again before it can run, the caller is told, as an error: 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, 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); not yet taken by the router (§12); or refused, with the approved too long ago or already run (each asks again, §15); not yet taken by the router (§12); or refused, with the
@@ -251,22 +298,26 @@ file; a node where the file is readable beyond the operator's account is a findi
with no argument, the calls asked on that machine in the last day, newest first, each with its state and with no argument, the calls asked on that machine in the last day, newest first, each with its state and
fingerprint, read from §13's state, and the marks it applies, each module's marked tools and verbs with their fingerprint, read from §13's state, and the marks it applies, each module's marked tools and verbs with their
source (its module's entry in the catalog half or its seat's definition) and the catalog revision read; given an ask's id, its whole source (its module's entry in the catalog half or its seat's definition) and the catalog revision read; given an ask's id, its whole
arguments and the act's digest; given `cancel` and an ask's id, it takes that open ask back (`Client.Cancel`), arguments and the act's digest; given `record` and an ask's id, it asks `warranted` again for that one ask by hand,
recorded first in the hand-act log through `mesh-hand-acts.hand-act` with who and why, and refused when the record in the kept-call form of §16, recorded in the hand-act log first; given `cancel` and an ask's id, it takes that open ask back (`Client.Cancel`),
recorded first in the hand-act log through `mesh-hand-acts.hand-act` with why, the verb taking who from the caller
it proves (Q-HA), and refused when the record
cannot be written. `mesh_describe` answers, for a marked tool or verb, `needs-approval` with its source. *Why:* cannot be written. `mesh_describe` answers, for a marked tool or verb, `needs-approval` with its source. *Why:*
ADR 0286 §25 (the runner's `approvals`), named as the mesh MCP server's own tools are; a tool reads everything a ADR 0286 §25 (the runner's `approvals`), named as the mesh MCP server's own tools are; a tool reads everything a
module holds and does by hand what it does by itself (the operator's instruction of 2026-10-25). *How it is module holds and does by hand what it does by itself (the operator's instruction of 2026-10-25). *How it is
checked:* unit, rows in the mesh MCP server's tool table test: the list, the marks with their sources, an ask read checked:* unit, rows in the mesh MCP server's tool table test: the list, the marks with their sources, an ask read
whole, a cancel recorded and one refused without a record, and `mesh_describe` of a marked tool. whole, a `record` that asks `warranted` again for one ask, a cancel recorded and one refused without a record, and
`mesh_describe` of a marked tool.
## Failing closed ## Failing closed
19. **Nothing runs when a part of the check is missing, and a stuck gate is said.** A call of a marked tool is 19. **Nothing runs when a part of the check is missing, and a stuck gate is said.** A call of a marked tool is
refused, runs nothing and says why when: no module holds `operator-channel` (read from the assigner's first view refused, runs nothing and says why when: no module holds `operator-channel` (read from the assigner's first view
`mesh-delivery-assigner.views.seat-holders`, which the tool runner consumes; the seat list names no holders); the `mesh-delivery-assigner.views.seat-holders`, which the tool runner consumes; the seat list names no holders); the
served entry of the module cannot be read (§5), which refuses every marked call on that runtime; the
router refuses the ask; the ask cannot be published; the router's record cannot be read; §13's state cannot be read 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, the failed read or write being made or written, in which case no marked call runs on that machine until it can, the failed read or write being made
again as a kept call (ADR 0668 §1) until it succeeds; or the marks are not yet read. Before again in the kept-call form (ADR 0668 §7; design 47 G §351), kept open by the failing key, until it succeeds; or the marks are not yet read. Before
the first view of the catalog half is read, a call waits for it within its 5 seconds and is then judged, asking the first view of the catalog half is read, a call waits for it within its 5 seconds and is then judged, asking
nothing while the marks are unread; still unread, it is refused, naming `mesh_first_views`. When §13's state has nothing while the marks are unread; still unread, it is refused, naming `mesh_first_views`. When §13's state has
been unreadable or unwritable for five minutes, the runner raises its finding `<node>.approvals.approvals-unreadable` been unreadable or unwritable for five minutes, the runner raises its finding `<node>.approvals.approvals-unreadable`
@@ -275,11 +326,16 @@ file; a node where the file is readable beyond the operator's account is a findi
written in the runner's log with the ask's id and fingerprint, never the arguments. *Why:* ADR 0286 §40, §42; a written in the runner's log with the ask's id and fingerprint, never the arguments. *Why:* ADR 0286 §40, §42; a
gate that refuses every call for a reason nobody hears is a silent fault, and the finding and its five minutes are gate that refuses every call for a reason nobody hears is a silent fault, and the finding and its five minutes are
the design's choice. *How it is checked:* unit, one row of §22's table per case, the holder read from a fixture the design's choice. *How it is checked:* unit, one row of §22's table per case, the holder read from a fixture
`views.seat-holders` with none and with one; and a row where the state fails, the finding is raised after five `views.seat-holders` with none and with one; a row where the served entry cannot be read and every marked call on
that runtime is refused; and a row where the state fails, the finding is raised after five
minutes, the state recovers with no further call, and the finding clears. minutes, the state recovers with no further call, and the finding clears.
20. **The terminal is not exempt.** A marked tool called through `mesh-cli` or any caller asks as every call does: the 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 code review reads that the gate sits runner cannot tell who called. *Why:* ADR 0286 §41. The gate wraps every handler the runner serves, a bundle's
on the call's handler, before the bundle, and that no path to a bundle passes around it. tools and seat verbs and the runner's own tools beside them (the ledger repairs) alike (Q-LR); the act, the ask,
the check, the claim, the recording and the refusals of §8 to §19 are all in `node-tools/internal/approval` (Q-PK).
*How it is checked:* the code review reads that the gate sits on every handler, before the bundle, and that no path
to a bundle or a runner's own tool passes around it; unit, a row of §22's table: a runner's own tool marked in a
fixture is refused unasked, never run.
## Marked first ## Marked first
@@ -287,10 +343,11 @@ file; a node where the file is readable beyond the operator's account is a findi
marks the verb `execute` with `needs-approval`; the zsh module's manifest (mesh-catalog `modules/zsh`), which holds 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`. The control-node keeps withholding `execute`, and whether it serves the seat, gains `operator-channel` in `uses`. 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 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 tools on every node (the seat `node-reads` of issue 418, resolved 2026-10-12, which `node-files` replaces whole, ADR
2026-10-16). *How it is checked:* live, as the acceptance says. 0394 §9). *How it is checked:* live, as the acceptance says.
- **The forge's two acts on a trunk's protection are marked.** The forge module (mesh-catalog `modules/gitea`) - **The forge's two acts on a trunk's protection are marked.** The forge module (mesh-catalog `modules/gitea`)
gains a tool `gitea_branch_protection_loosen`, marked in its `needs-approval`, which alone performs the two acts gains a tool `gitea_branch_protection_loosen`, marked in its `needs-approval`, taking `owner`, `repo`, `branch`,
`act` (`remove-protection` or `allow-force-push`) and `why` (Q-FG), which alone performs the two acts
`gitea_branch_protection_set_full` refuses today: removing a trunk's protection, and allowing a force-push onto `gitea_branch_protection_set_full` refuses today: removing a trunk's protection, and allowing a force-push onto
a trunk; `gitea_branch_protection_set_full` keeps refusing both and its refusal names the new tool. The manifest a trunk; `gitea_branch_protection_set_full` keeps refusing both and its refusal names the new tool. The manifest
gains `operator-channel` in `uses`. No other tool or verb is marked in this milestone. *Why:* the forge's own gains `operator-channel` in `uses`. No other tool or verb is marked in this milestone. *Why:* the forge's own
@@ -300,9 +357,17 @@ file; a node where the file is readable beyond the operator's account is a findi
principle: loosening a trunk's protection runs only on the operator's own warrant for that one act, never on a principle: loosening a trunk's protection runs only on the operator's own warrant for that one act, never on a
session's word. Its bound: a process that reads the forge module's own credential speaks to the forge directly session's word. Its bound: a process that reads the forge module's own credential speaks to the forge directly
and goes around it, the same known limit as issue 1966 note 193; the prepare reads that credential's mode and and goes around it, the same known limit as issue 1966 note 193; the prepare reads that credential's mode and
owner on the forge's node with `<node>/node-reads.stat` and writes them in the milestone file. *How it owner on the forge's node with `<node>/node-files.stat` (§21a) and writes them in the milestone file. *How it
is checked:* the forge module's tool test: `set_full` refuses both acts naming the new tool; the new tool is checked:* the forge module's tool test: `set_full` refuses both acts naming the new tool; the new tool
performs each; and live, as the acceptance says. performs each; and live, as the acceptance says.
21a. **`node-files.stat` answers a mode under a directory the account cannot enter.** The seat `node-files`' holder
(mesh-catalog `modules/mesh-files`) answers `stat` of a path its account cannot reach through `sudo -n` with its
fixed `/usr/bin/stat`, as its writes already go (ADR 0394 §2), answering the path's mode, owner, group and kind and
nothing of its content; the secret places ADR 0394 §7 names are stated, never read. Its README's "not yet through
sudo" loses `stat`. *Why:* the prepare could not read the forge credential's mode (step 0b); ADR 0394 §2 has the
holder use sudo where it must. The lead's handover named the retired `node-reads` (ADR 0394 §9), whose successor this
is. *How it is checked:* unit, a row in mesh-files' `Stat` test: a file under a directory of mode 0700 owned by
another account answers its mode and owner through sudo, with no content.
## Tests, delivery and going back ## Tests, delivery and going back
@@ -313,15 +378,19 @@ file; a node where the file is readable beyond the operator's account is a findi
operator's Approve written as the router's record, the call again, the run, the hand-act, `mesh_approvals`, and a operator's Approve written as the router's record, the call again, the run, the hand-act, `mesh_approvals`, and a
restart between the run and `warranted`'s answer. The client's `KeyPrefix` (§10) has a row in mesh-contracts' restart between the run and `warranted`'s answer. The client's `KeyPrefix` (§10) has a row in mesh-contracts'
client test: every key of its own `State` written and read carries it, an empty prefix writes today's keys, and client test: every key of its own `State` written and read carries it, an empty prefix writes today's keys, and
with a prefix set `Where` still reads `<module>.<id>`. *Why:* the operator's test budget (one whole test over many small with a prefix set `Where` still reads `<module>.<id>`. The mesh MCP server's `TestMeshWaitIsAnnouncedAmongSeven` counts eight
ones). *How it is checked:* the check counts the module's tests against its budget. tools with `mesh_approvals` (Q-T). New test functions are allowed within each module's budget of 500 unit and 50
integration tests, and a module over it shrinks toward it with each change (Q-TB). *Why:* the operator's test
budget (one whole test over many small ones). *How it is checked:* the check counts each module's tests against
its budget.
23. **In G's delivery group, ordered by the module graph.** The members are pushed and checked in design 47 milestone 23. **In G's delivery group, ordered by the module graph.** The members are pushed and checked in design 47 milestone
G's group, with design 34's milestone every-event. The orders are the module graph's: the indexer's manifest G's group, with design 34's milestone every-event. The orders are the module graph's: the indexer's manifest
validator and catalog graph entry that read `needs-approval` before any manifest or seat definition carrying it (a validator and catalog graph entry that read `needs-approval` before any manifest or seat definition carrying it (a
reader edge, design 47 G §89); and the grants part that composes the deny of §6 before the seat definition marking reader edge, design 47 G §89, whose list covers `needs-approval`; Q-RE); and the grants part that composes the deny of §6 before the seat definition marking
`execute` (an edge of the seat definition's readers), so no machine serves `execute` unasked between the two. `execute` (an edge of the seat definition's readers), so no machine serves `execute` unasked between the two; and
the grants part serving `withheld` before a node-engine calls it (a served-verb edge, §6).
*Why:* the operator, 2026-11-11, "Build the gate in G"; the operator, 2026-11-03, a change is designed as it should *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. be and its delivery is the delivery system's. *How it is checked:* the delivery plan names the three edges.
24. **Going back is going forward.** A fault met after the delivery is fixed by a new change delivered the same way. 24. **Going back is going forward.** A fault met after the delivery is fixed by a new change delivered the same way.
Reverting instead reverts G's whole group in the reverse of §23's order: the seat definition's mark first, then the Reverting instead reverts G's whole group in the reverse of §23's order: the seat definition's mark first, then the
grants part, then the validator. The `approvals` state is then declared by no running module: it is an orphan grants part, then the validator. The `approvals` state is then declared by no running module: it is an orphan
@@ -341,6 +410,8 @@ file; a node where the file is readable beyond the operator's account is a findi
`gitea_branch_protection_loosen` removing the trunk's protection answers "Not run" and asks on the phone; after the `gitea_branch_protection_loosen` removing the trunk's protection answers "Not run" and asks on the phone; after the
Approve the same call runs once; then `gitea_branch_protection_set_full` sets the full protection again and Approve the same call runs once; then `gitea_branch_protection_set_full` sets the full protection again and
`gitea_branch_protection_get` reads it back as it was (§21). `gitea_branch_protection_get` reads it back as it was (§21).
- **A dropped mark still asks while the old bundle runs** (live, read by tool call): a delivery that drops a module's
mark asks a call of that tool until the node runs the new bundle, read with `mesh_approvals` (§5).
- **No machine serves `execute` unasked** (live, read by tool call): before this acceptance passes, on every node - **No machine serves `execute` unasked** (live, read by tool call): before this acceptance passes, on every node
`execute` is either asked as above or absent, read with each node's `node-engine.composition` and `execute` is either asked as above or absent, read with each node's `node-engine.composition` and
`mesh-grants.grants_user_list`'s `withheld` (§6). `mesh-grants.grants_user_list`'s `withheld` (§6).