diff --git a/03-DESIGN/01-to-be/46-milestones/7-approval-mark.md b/03-DESIGN/01-to-be/46-milestones/7-approval-mark.md index 5f97ca43..9fd4e856 100644 --- a/03-DESIGN/01-to-be/46-milestones/7-approval-mark.md +++ b/03-DESIGN/01-to-be/46-milestones/7-approval-mark.md @@ -81,7 +81,8 @@ file; a node where the file is readable beyond the operator's account is a findi 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: a bundle whose marks name a tool it does not - register is not loaded, failing closed rather than serving unmarked what the manifest meant to mark, and that is its + register is not loaded, so no tool of that module is served on that node at all, failing closed rather than serving + unmarked what the manifest meant to mark, and that is its finding `..mark-unserved` (said as §16's), cleared when a bundle that serves it, or a manifest without the mark, is loaded. 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 @@ -115,7 +116,10 @@ file; a node where the file is readable beyond the operator's account is a findi subject of every module the runtime carries: `mesh.mod..tool.` and the same followed by `.*`; the caller-named subject mesh-sdk's `process.Call` sends on first, `mesh.mod..call..>`; and for a marked verb `mesh.seat..tool.`, the same followed by `.`, and `mesh.seat..call...>`. - The bus's deny outranks its allow of `mesh.mod..tool.>` for each. Going back to an older tool runner on a + The bus's deny outranks its allow of `mesh.mod..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-tools` last said running (design 47 G §309); + readings that revision names from 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 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 @@ -153,8 +157,9 @@ file; a node where the file is readable beyond the operator's account is a findi checked:* unit, two rows of §22's table. 10. **One ask, under the module's name.** Otherwise the runner asks through an `operatorchannelclient.Client` whose `Module` is the marked module, whose `State` is the runner's state of §13, and whose new field `KeyPrefix`, which - the client in mesh-contracts gains and puts before every key it writes and reads (empty for every asker today), is - `.`; unless an ask for the same act is + the client in mesh-contracts gains and puts before every key of its own `State` it writes and reads (empty for + every asker today), is `.`; never before the router's record it reads through `Record` and `Where`, whose key + stays `.` 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` `operator`; `expires` 60 minutes after; no `on-expiry`; two options, `approve` (label Approve) and `decline` (label Decline), both at level `approve`, each binding the act's digest; the headline "Run on ?", @@ -195,8 +200,9 @@ file; a node where the file is readable beyond the operator's account is a findi 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 reached, or any other) fails closed under §19 and is said in the answer; `mesh_approvals` reports the bucket's fill - against its cap. 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. + 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 + 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, kept 48 hours, by one writer per node; the client built since keeps asks and marks a warrant acted on by compare-and-set in a module's bus state (ADR 0259 §10, ADR 0627 §1), and a per-machine state (ADR 0595) is one @@ -287,7 +293,11 @@ file; a node where the file is readable beyond the operator's account is a findi 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; the operator, 2026-11-11: "Mark them in G". *How it + and a marked form, and tightening a protection stays unasked; the operator, 2026-11-11: "Mark them in G". The + 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-reads.stat` 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. @@ -299,7 +309,8 @@ file; a node where the file is readable beyond the operator's account is a findi test that starts the runner against the real bus, with no store, with a fixture marked module: a call, the 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' - client test: every key written and read carries it, and an empty prefix writes today's keys. *Why:* the operator's test budget (one whole test over many small + 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 `.`. *Why:* the operator's test budget (one whole test over many small ones). *How it is checked:* the check counts the module's tests against its budget. 23. **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 @@ -322,7 +333,8 @@ file; a node where the file is readable beyond the operator's account is a findi `operator-channel.asks`, and after the operator's Approve the same call runs once, a third identical call is refused as already run, and `mesh-hand-acts.hand-acts` names the warranted act (§10, §12, §14 to §16, §21). - **The forge's loosening is asked, and the protection is put back** (live, read by tool call): on a scratch repository - of the forge made for this acceptance, `acceptance-approval-mark`, with full protection on its trunk, + `acceptance-approval-mark`, created by the acceptance through the forge's `gitea_create_repo` and kept afterwards, + never deleted, with full protection set on its trunk by `gitea_branch_protection_set_full`, `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).