Write the operator's answers on the approval mark into milestone 7
Call again stands by the operator's choice; the credential file's reach is a known limit, as G's shared account is; and the forge's two loosening acts move to a marked tool of their own, so tightening stays unasked. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -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-catalog modules/zsh]
|
||||
code: [mesh-sdk, mesh-contracts, mesh-delivery, mesh-tools, mesh-catalog modules/zsh, mesh-catalog modules/gitea]
|
||||
decisions:
|
||||
- "ADR 0286"
|
||||
- "ADR 0259"
|
||||
@@ -35,8 +35,8 @@ gate in G").
|
||||
|
||||
**Where.** mesh-sdk (the manifest field, the seat definition's field, the per-machine state's keys), mesh-contracts
|
||||
(the login shell's seat definition), mesh-delivery (the indexer's manifest validator and catalog graph entry, the grants
|
||||
part's deny), mesh-tools (the tool runner's gate, its state, its tools) and the mesh MCP server's `mesh_describe` in
|
||||
it. Nothing is built in a controller: no controller issues memberships today, bus permissions are the grants part's
|
||||
part's deny), mesh-tools (the tool runner's gate, its state, its tools, and the mesh MCP server's `mesh_describe` in
|
||||
it) and mesh-catalog (the zsh module, which holds the login shell, and the forge module, §21). Nothing is built in a controller: no controller issues memberships today, bus permissions are the grants part's
|
||||
(seat `mesh-grants`), the hand-act log is the seat `mesh-hand-acts`, and the asker's logic is operator-channel's client
|
||||
in mesh-contracts (ADR 0627 §1).
|
||||
|
||||
@@ -46,13 +46,16 @@ digest is Go alone (§8), the claim is kept in the runner's bus state and not a
|
||||
and not a retry on a timer (§16), and a fourth open ask is offered as G makes it (§17). The controller's own verbs (ADR
|
||||
0286 §43) are not built: no controller serves verbs.
|
||||
|
||||
**The bound of the gate.** The gate holds for every call that reaches the tool runner's handler. It does not hold for
|
||||
a process that reads the tool runner's bus credential (the file `broker` in its state directory, `own-secrets`) and
|
||||
speaks to the bus itself: its account may subscribe a marked subject on a machine whose runner honours the mark. Who
|
||||
may read that file is therefore the bound of the operator's choice; the prepare reads its mode on every machine with
|
||||
`<node>/node-reads.stat` and writes it in the milestone file, and a mode readable beyond the operator's account is a
|
||||
design question for the operator before develop. The same limit as design 47 G §385 (one tool-runner account per
|
||||
machine).
|
||||
**The bound of the gate, a known limit.** The gate holds for every call that reaches the tool runner's handler. It does
|
||||
not hold for a process that reads the tool runner's bus credential (the file `broker` in its state directory,
|
||||
`own-secrets`) and speaks to the bus itself: 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
|
||||
file is mode 0600, owned by the operator's account, in a directory of mode 0755: 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
|
||||
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. The
|
||||
prepare reads the file's mode and owner on every node with `<node>/node-reads.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 mark
|
||||
|
||||
@@ -70,14 +73,19 @@ machine).
|
||||
4. **The manifest validator refuses a mark it cannot honour.** The indexer's manifest validator (`judge/rules.go`)
|
||||
refuses, as the checker runs it, and sets aside to wait, as the indexer runs it, a manifest that: marks a tool its
|
||||
`tools` does not list, or a verb its `claims` do not serve (ADR 0286 §4); marks a tool or a verb, or holds a seat
|
||||
verb its seat marks, without `operator-channel` in `uses` (§7); marks a tool or a verb and declares a resource of
|
||||
type `process`, whose tools its own program answers and no tool runner gates; or names in `needs-approval` one name
|
||||
that is both one of its tools and one of its events. Each refusal names the entry and the rule. *Why:* a mark
|
||||
verb its seat marks, without `operator-channel` in `uses` (§7); marks a tool or a verb while every artifact of kind
|
||||
`bundle` it builds is run by one of its `process` resources (`artifact`), so its own program answers its tools and
|
||||
no tool runner loads or gates them (a module such as the forge's, whose tools bundle no process runs, is not
|
||||
refused); or names in `needs-approval` one name that is both one of its tools and one of its events. A tool mark
|
||||
is checked against `tools` where the manifest lists its tools; where only its bundle names them, the tool runner
|
||||
checks each marked tool against the tools the bundle registers at load, and a mark naming none is its finding
|
||||
`<node>.<module>.mark-unserved` (said as §16's), cleared when the bundle serves it or the mark goes. Each refusal names the entry and the rule. *Why:* a mark
|
||||
nothing enforces would be a call that runs unasked while saying it is guarded; ADR 0286 §43 had the controller
|
||||
enforce its own verbs, and no other own-process server has a gate, so such a mark is refused rather than ignored;
|
||||
and an entry that names two things is read one way by one reader and another way by the next (the design's choices
|
||||
under ADR 0286 §4, §40). *How it is checked:* unit, one row per refusal in `judge/rules_test.go`, and a row that
|
||||
accepts the zsh module's manifest of §21.
|
||||
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
|
||||
`mark-unserved`.
|
||||
|
||||
## Where the marks are read, and where they are withheld
|
||||
|
||||
@@ -156,9 +164,9 @@ machine).
|
||||
run, asked; not yet taken by the router (ask <id>, fingerprint …); make the same call again later." The ask stays
|
||||
in the seat's stream of accepts, a work queue the router takes it from when it runs, and the `act.` key stays, so
|
||||
the next call reads the router's record of the same ask and asks nothing new. A mark added since the caller last
|
||||
called is named in the answer ("marked since <revision>"). *Why:* ADR 0286 §14, §37. The caller calls again: the
|
||||
held call the operator asked for on 2026-10-16 (issue 417), which runs on the Approve and returns its outcome, is
|
||||
with the operator; this line is written for "call again" until the operator answers. *How it is checked:* unit,
|
||||
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.
|
||||
|
||||
## The runner's record, and the same call again
|
||||
@@ -249,7 +257,17 @@ machine).
|
||||
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). No other tool or verb is marked in this milestone. *How it is checked:* live, as the acceptance says.
|
||||
2026-10-16). *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`, which alone performs the two acts
|
||||
`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
|
||||
gains `operator-channel` in `uses`. No other tool or verb is marked in this milestone. *Why:* the forge's own
|
||||
refusal says these two acts are the operator's and waited for this mark (issue 417, 2026-10-28); a mark is on a
|
||||
whole tool (ADR 0286 §1), so the acts move to a tool of their own, as ADR 0286 §48 splits a tool into an unmarked
|
||||
and a marked form, and tightening a protection stays unasked; marked now pending the operator's answer of
|
||||
2026-11-11. *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, a loosening asked on the phone runs once on the Approve.
|
||||
|
||||
## Tests, delivery and going back
|
||||
|
||||
|
||||
Reference in New Issue
Block a user