Answer step 0b's review of the approval mark and marked waits
mesh/delivery merged: source.main moved to novox/hq@f1fe609b3e85
mesh/check being carried to 8e9116469a0a on f1fe609b3e85 | from fbd7981b7be2
mesh/delivery merged: source.main moved to novox/hq@f1fe609b3e85
mesh/check being carried to 8e9116469a0a on f1fe609b3e85 | from fbd7981b7be2
The served version comes from what the bundle runs and a reading from the runner that made it, so no placement or clock lifts a deny; the runner's state takes the old form the bus module can create; kept calls stay open on their key; and the closed directory's mode is read by node-files. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -124,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
|
||||
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
|
||||
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
|
||||
@@ -183,7 +184,8 @@ that holds its mark reach the machines together.
|
||||
answered after the timeout with §16's words; `TestMeshWaitWithNoFamiliesIssuedWaitsOnNothing` is deleted. In
|
||||
`bus/wait_test.go`, `TestTheBusRefusesAWaitOutsideTheGrant` stays as §16's refusal test, with a timeout of a
|
||||
fraction of a second, its comment citing ADR 0670 §1. 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. New
|
||||
`operator-channel` in `uses`, the state `approvals` in the old `state` section, per machine with its time to live,
|
||||
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
|
||||
@@ -317,8 +319,8 @@ approves, for a marked event or a restricted stream.
|
||||
`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` and the tool `mesh_wait`,
|
||||
its build (the tool runner's own fingerprint, from its catalog entry of the version served; design 46 7-approval-mark
|
||||
§8, Q-BD), 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
|
||||
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
|
||||
@@ -350,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
|
||||
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
|
||||
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
|
||||
`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`.
|
||||
|
||||
@@ -3,7 +3,7 @@ layer: to-be
|
||||
design: 46
|
||||
milestone: 7-approval-mark
|
||||
updated: 2026-11-11
|
||||
code: [mesh-sdk, mesh-contracts, mesh-delivery, mesh-tools, mesh-host, 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:
|
||||
- "ADR 0286"
|
||||
- "ADR 0259"
|
||||
@@ -14,6 +14,8 @@ decisions:
|
||||
- "ADR 0595"
|
||||
- "ADR 0597"
|
||||
- "ADR 0310"
|
||||
- "ADR 0394"
|
||||
- "ADR 0619"
|
||||
- "ADR 0268"
|
||||
- "ADR 0564"
|
||||
---
|
||||
@@ -57,10 +59,10 @@ hear a restricted stream without a warrant. This is the same known limit as desi
|
||||
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
|
||||
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. The forge module's
|
||||
own credential (§21) lies in a directory of mode 0700, owned by the forge's account, which `node-reads` cannot enter:
|
||||
no tool answers that file's own mode, a missing read tool, said here; the directory's mode is what the prepare read.
|
||||
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
|
||||
|
||||
@@ -114,45 +116,56 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
definitions of the catalog half carry the verb marks (§2). The tool runner, which from G takes its first view of the
|
||||
catalog half by `mesh-catalog.revision` and follows `catalog-revision` (design 47 G §196), derives from it, for each
|
||||
module it serves, its marked tools and verbs, in a package of its own, `node-tools/internal/approval`, again on
|
||||
every revision it hears. No module's name is written in its code. The marks follow the running version: the runner
|
||||
applies the marks of the module's version it serves, read from the assignment half's entry for that module on its
|
||||
node (design 47 G §175; Q-VS), and, while the newest revision's entry for that module
|
||||
differs, the union of both, so a mark is never lost before or after the module moves. A mark added while a call
|
||||
waits applies from the next call, which the answer of §12 says. *Why:* ADR 0286 §5 had the controller issue the mark
|
||||
in a membership so that a bundle could not drop it; no controller issues memberships now, and the judged catalog
|
||||
half is the same guarantee: the bundle never carries the mark (ADR 0656 §6, one source for which modules exist).
|
||||
The union keeps ADR 0286 §3's "marks add up" across a version change. *How it is checked:* unit, a table test in
|
||||
`approval` derives the marks from fixture catalog halves, §3's row, a version change and the union among them.
|
||||
every revision it hears. No module's name is written in its code. The marks follow the running version: a bundle's
|
||||
served version is the fingerprint its composed environment carries (design 47 G §424), whose entry the runner reads
|
||||
by `mesh-catalog.entry` (design 47 G §443) when it loads the bundle; it applies that entry's marks and, while the
|
||||
newest revision's entry for that module differs, the union of both, so a mark is never lost before or after the
|
||||
module moves, and a delivery that drops a mark while the old bundle still runs is still asked. When the served
|
||||
entry cannot be read, every marked call on that runtime is refused (§19). A mark added while a call waits applies
|
||||
from the next call, which the answer of §12 says. *Why:* ADR 0286 §5 had the controller issue the mark in a
|
||||
membership so that a bundle could not drop it; no controller issues memberships now, and the judged catalog half is
|
||||
the same guarantee: the bundle never carries the mark (ADR 0656 §6, one source for which modules exist). The union
|
||||
keeps ADR 0286 §3's "marks add up" across a version change. 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
|
||||
`approval-mark/1` among its call capabilities (`runtime.CallCapabilities`), which its announcement carries and its
|
||||
node's node-engine takes into the node's record; the assignment half carries, for each node, its capability readings
|
||||
and the time each was taken (`at`), which the assigner keeps from the node-engine's reading (design 47 G §194;
|
||||
Q-CR). The grants part, composing a runtime's account, reads that runtime's node's readings: where the
|
||||
tool runner does not state `approval-mark/1`, where the node has no readings, or where its readings are older than
|
||||
the tool runner's version on that node (failing closed as §19), it composes a subscribe deny of every marked
|
||||
subject of every module the runtime carries, the marks read from the catalog half as §5 has them:
|
||||
`mesh.mod.<module>.tool.<tool>` and the same followed by `.*`; the
|
||||
caller-named subject mesh-sdk's `process.Call` sends on first, `mesh.mod.<module>.call.<tool>.>`; and for a marked
|
||||
verb of a seat held per node `mesh.seat.<seat>.tool.<verb>`, the same followed by `.<node>`, and
|
||||
`mesh.seat.<seat>.call.<verb>.<node>.>`; for a marked verb of a seat held once for the mesh
|
||||
`mesh.seat.<seat>.tool.<verb>` and `mesh.seat.<seat>.call.<verb>.>`, since its subjects name no node (Q-DL).
|
||||
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
|
||||
same assignment half revision, its entry for `node-tools` on that node (the module version placed there, design 47
|
||||
G §175), against the version the node-engine's `running.<node>.node-tools` last said running (design 47 G §309);
|
||||
readings whose `at` is before that version ran are older. Going back to an older tool runner on a
|
||||
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
|
||||
subscribe deny, as `PublishDeny` is for publishing; the user list's `User` gains `subscribe_deny` beside
|
||||
`publish_deny`, in its comparison and in the differences the grants part says (Q-SD). 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 read verb
|
||||
`withheld` that the seat `mesh-grants`'s definition gains (input `node`; answer each module's withheld subjects and
|
||||
why), never from the grants part's state (Q-NE). *Why:* ADR 0286 §7, §8, §9: the bus
|
||||
`approval-mark/1` among its call capabilities (`runtime.CallCapabilities`), which its announcement carries with
|
||||
the tool runner's own fingerprint, and its node's node-engine takes both into the node's record; the assignment
|
||||
half carries, for each node, its capability readings, each with the fingerprint of the runner that announced it
|
||||
(design 47 G §194; Q-CR). The grants part, composing a runtime's account, reads that runtime's node's readings and
|
||||
the fingerprint the node-engine's `running.<node>.node-tools` last said running (design 47 G §309): where the tool
|
||||
runner does not state `approval-mark/1`, where the node has no readings, or where the readings' fingerprint differs
|
||||
from the running one or is absent (an older reading, failing closed as §19; no time is compared), it composes a
|
||||
subscribe deny of every marked subject of every module the runtime carries, the marks read from the catalog half
|
||||
as §5 has them: `mesh.mod.<module>.tool.<tool>` and the same followed by `.*`; the caller-named subject mesh-sdk's
|
||||
`process.Call` sends on first, `mesh.mod.<module>.call.<tool>.>`; for a marked verb of a seat held per node
|
||||
`mesh.seat.<seat>.tool.<verb>`, the same followed by `.<node>`, and `mesh.seat.<seat>.call.<verb>.<node>.>`; and for
|
||||
a marked verb of a seat held once for the mesh `mesh.seat.<seat>.tool.<verb>` and `mesh.seat.<seat>.call.<verb>.>`,
|
||||
since its subjects name no node (Q-DL). The bus's deny outranks its allow of `mesh.mod.<module>.tool.>` for each.
|
||||
Going back to an older tool runner on a 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 subscribe deny, as `PublishDeny` is for publishing; the user list's `User` gains `subscribe_deny` beside
|
||||
`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
|
||||
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
|
||||
checked:* unit, the grants part's composition table: readings lacking the capability, no readings, and readings
|
||||
older than the runner each compose the deny of every subject above, the caller-named ones and a marked seat verb's
|
||||
`.<node>` form among them, each named in `withheld`; readings with the capability compose no deny; integration, the
|
||||
checked:* unit, the grants part's composition table: readings lacking the capability, no readings, readings with
|
||||
another runner's fingerprint and readings with none each compose the deny of every subject above, the caller-named
|
||||
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
|
||||
while it holds `mesh.mod.<module>.tool.>`, and so is a call sent through `process.Call` on the caller-named
|
||||
subject.
|
||||
@@ -168,7 +181,7 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
|
||||
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 module's fingerprint, from its catalog entry of the version served; Q-BD), 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
|
||||
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
|
||||
@@ -210,13 +223,18 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
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
|
||||
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
|
||||
|
||||
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), and no cap of
|
||||
its own: the `state` template's fixed size holds (ADR 0595 §2; Q-CAP).
|
||||
13. **The runner keeps its asks and claims in a state of its own, per machine.** The tool runner, a foundation part,
|
||||
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
|
||||
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
|
||||
@@ -227,7 +245,7 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
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. 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
|
||||
against the template's size, 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
|
||||
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
|
||||
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,
|
||||
@@ -236,7 +254,9 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
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
|
||||
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.
|
||||
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
|
||||
@@ -256,15 +276,17 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
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
|
||||
<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) through mesh-sdk's `busretry` (Q-KC), and on its answer writes `recorded` on
|
||||
`<node>.ask.<id>`. At each start the runner calls `warranted` again for every acted, unrecorded ask its keys hold,
|
||||
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
|
||||
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
|
||||
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
|
||||
`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
|
||||
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:
|
||||
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
|
||||
@@ -276,23 +298,26 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
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
|
||||
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,
|
||||
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:*
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
`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
|
||||
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, through `busretry`) 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
|
||||
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`
|
||||
@@ -301,14 +326,16 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
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
|
||||
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.
|
||||
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. The gate wraps every handler the runner serves, a bundle's
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -316,8 +343,8 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
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
|
||||
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). *How it is checked:* live, as the acceptance says.
|
||||
tools on every node (the seat `node-reads` of issue 418, resolved 2026-10-12, which `node-files` replaces whole, ADR
|
||||
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`)
|
||||
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
|
||||
@@ -330,9 +357,17 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
@@ -352,9 +387,10 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
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
|
||||
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
|
||||
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.
|
||||
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
|
||||
@@ -374,6 +410,8 @@ no tool answers that file's own mode, a missing read tool, said here; the direct
|
||||
`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
|
||||
`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
|
||||
`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).
|
||||
|
||||
Reference in New Issue
Block a user