From c232b1fb819d929ebe06e33e82eba1bf700cd637 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 01:25:22 +0100 Subject: [PATCH] Design 34 every-event: write in the four domain reviews' findings The reviews found waits that time out for ever on a misspelt name, refusals that would loop a watcher, an acceptance no answer could prove, readers still describing the list, and no way back said. Each is now a design line; the stream-events lines are written for the recommended option and marked pending the operator's answer. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../01-to-be/34-milestones/every-event.md | 282 +++++++++++------- 03-DESIGN/01-to-be/34-the-console.md | 17 +- 2 files changed, 196 insertions(+), 103 deletions(-) diff --git a/03-DESIGN/01-to-be/34-milestones/every-event.md b/03-DESIGN/01-to-be/34-milestones/every-event.md index 95408e5e..b1544a18 100644 --- a/03-DESIGN/01-to-be/34-milestones/every-event.md +++ b/03-DESIGN/01-to-be/34-milestones/every-event.md @@ -3,13 +3,16 @@ layer: to-be design: 34 milestone: every-event updated: 2026-11-11 -code: [mesh-sdk, mesh-delivery, mesh-tools] +code: [mesh-sdk, mesh-delivery, mesh-tools, mesh-catalog modules/claude-code] decisions: - "ADR 0670" - "ADR 0367" - "ADR 0043" - "ADR 0259" - "ADR 0332" + - "ADR 0363" + - "ADR 0376" + - "ADR 0489" - "ADR 0571" - "ADR 0578" - "ADR 0614" @@ -18,37 +21,45 @@ decisions: # Design 34, milestone every-event — the tool runner hears every event **What it builds.** A session's `mesh_wait` waits on any event of any seat or module, by its name, with no list to -keep: the tool runner's bus account is granted the two event subjects of the whole mesh, and the list of families in -the tool runner's manifest, the manifest validator's rule about it and the grants part's computation of subjects from -it go (ADR 0670). The whole shape is in the design's main file, [§3b](../34-the-console.md#3b-waiting-on-the-bus); -this part holds every line the milestone builds, cited as "design 34 every-event §n". +keep: the tool runner's bus account is granted the event subjects of the whole mesh, and the list of families in the +tool runner's manifest, the manifest validator's rule about it and the grants part's computation of subjects from it +go (ADR 0670). The whole shape is in the design's main file, [§3b](../34-the-console.md#3b-waiting-on-the-bus); this +part holds every line the milestone builds, cited as "design 34 every-event §n". -**Where.** Three repositories, one delivery group: mesh-sdk (the shared event subjects, and the field, type and -membership entry that go), mesh-delivery (the grants part, seat `mesh-grants`; the manifest validator in the indexer) -and mesh-tools (the tool runner, module `node-tools`). The milestone is built on each repository's main as it stands; -design 47's milestone G, which edits the grants part's account composition and the tool runner's manifest and manifest -test too, is rebased onto it. +**Where.** Four repositories and hq, one delivery group: mesh-sdk (the shared event subjects; the field, type and +membership entry that go), mesh-delivery (the grants part, seat `mesh-grants`; the manifest validator in the indexer), +mesh-tools (the tool runner, module `node-tools`) and mesh-catalog (the claude-code module's Stop hook, §20), with hq +for the playbook and agent lines (§24). Only these reference the list today: no other manifest carries the key +`waits`. The milestone is built on each repository's main as it stands. Design 47's milestone G edits the grants +part's account composition and the tool runner's manifest and manifest test too; its lead rebases it onto this +milestone's heads when G's develop restarts. + +**Pending lines.** The lines marked *Pending the operator's answer (stream events)* are written for the recommended +option of a question open with the operator: whether the tool runner also hears the events kept in a module's own +stream (ADR 0363 §2). They are kept, with the mark removed, if the operator chooses it, and deleted if not; no other +line depends on them. ## The event subjects, in mesh-sdk 1. **One place names the mesh's event subjects.** mesh-sdk's package `buscompose` gains a file `events.go` with three - exports, used by the grants part (§5) and the tool runner (§12), so neither spells the event namespace itself - (shared code belongs in mesh-sdk): - - `EveryEvent`, the two subjects the tool runner's account hears: `mesh.seat.*.event.>` and `mesh.mod.*.event.>`, - in that order (ADR 0670 §1); + exports, used by the grants part (§6) and the tool runner (§13), so neither spells the event namespace itself: + - `EveryEvent`, the subjects the tool runner's account hears: `mesh.seat.*.event.>` and `mesh.mod.*.event.>`, in + that order (ADR 0670 §1); - `EventSubjects(name)`, the subjects one event name lands on. A name is `.`, optionally followed by further tokens (`operator-channel.decided.claude-code`); each token matches `[A-Za-z0-9_-]+`. It answers four subjects: `mesh.seat..event.`, the same followed by `.>`, `mesh.mod..event.`, and - the same followed by `.>`, where `` is every token after the first. It refuses, with an error naming the - rule, a name of fewer than two tokens, an empty token, or a token outside that form (so `*` and `>` are refused); + the same followed by `.>`, where `` is every token after the first; given a namespace (`seat` or `module`, + §13) it answers only that namespace's two. It refuses, with an error naming the rule, a name of fewer than two + tokens, an empty token, or a token outside that form (so `*` and `>` are refused); - `EventName(subject)`, the inverse for a heard subject: for `mesh.seat..event.` or - `mesh.mod..event.` it answers the name `.` and whether the emitter is a seat; any - other subject is answered as not an event. + `mesh.mod..event.` it answers the name `.` and its namespace; any other subject is + answered as not an event. Why four subjects: a name does not say whether its emitter is a seat or a module, and both namespaces are heard - (§5), so both are subscribed; and a seat's event that names its asker or kind after the event (ADR 0259 §3), or a - module's that names its module and branch, is heard by the event's name alone, the `filter` narrowing it by body. - How it is checked: one table-driven unit test of the three, in `events_test.go`. + (§6); and a seat's event that names its asker or kind after the event (ADR 0259 §3), or a module's that names its + module and branch, is heard by the event's name alone, the `filter` narrowing it by body. How it is checked: one + table-driven unit test of the three, in `events_test.go`, one of whose rows holds that every subject + `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`; 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 @@ -65,33 +76,35 @@ test too, is rebased onto it. ## The grant, in the grants part (seat `mesh-grants`) -5. **The tool runner's runtime hears every event subject.** In `internal/buscompose/nats.go`, where a runtime's - permissions are composed from the modules it carries, the call of `waitGrants` is replaced by: when one of the - carried modules says `tool-runner` (`Declared.ToolRunner`), `buscompose.EveryEvent` (§1) is appended to the - runtime account's subscribe list. Nothing else is added for it: no publish, no durable consumer, no queue, no other - subject; a runtime carrying no tool runner gets neither subject (ADR 0670 §1, §3; ADR 0043 §2, §3, §5, §8 as ADR - 0670 §3 now reads them). The subjects are written by the grants part into the bus's user list as every grant is - (ADR 0571 §9), read with the grants part's own tools as today. -6. **What the grants part computed from the list goes.** `internal/buscompose/waits.go` is deleted whole (`Wait`, +5. **What the grants part computed from the list goes.** `internal/buscompose/waits.go` is deleted whole (`Wait`, `WaitSubject`, `waitGrants`, `waitsIssuedFor`); `Declared.Waits` goes from `internal/buscompose/users.go`, and with it the term `len(d.Waits) > 0` of `runnerServes`; `m.Waits = waitsIssuedFor(d)` goes from `membership.go`, and the alias `WaitIssued` from `sdk_membership.go`; `waitsOf` and the field `Waits:` of `declaredFor` go from `internal/grants/records.go` (ADR 0670 §2). `safeSubject` in `nats.go` stays: the bus-state grants use it. -7. **The tool runner's membership names no events.** The membership the grants part writes for the tool runner in - its state `memberships` (key `.node-tools`, ADR 0571 §10) carries no `waits` key, since the field is gone - (§4); every other part of it is unchanged. +6. **The tool runner's runtime hears every event subject.** In `internal/buscompose/nats.go`, where a runtime's + permissions are composed from the modules it carries, the call of `waitGrants` is replaced by: when one of the + carried modules says `tool-runner` (`Declared.ToolRunner`), `buscompose.EveryEvent` (§1) is appended to the + runtime account's subscribe list. Nothing else is added for it: no publish, no durable consumer, no queue, no other + subject; a runtime carrying no tool runner gets neither subject (ADR 0670 §1, §3; ADR 0043 §2, §3, §5, §8 as ADR + 0670 §3 now reads them). The grants part writes them into the bus's user list as every grant (ADR 0571 §9); they + are read with `mesh-grants.grants_user_list`, and composed at once with `mesh-grants.grants_compose_now`. +7. **The tool runner's membership names no events.** The grants part's first composition after its delivery rewrites + every tool runner's membership in its state `memberships` (key `.node-tools`, ADR 0571 §10) without the key + `waits`, since the field is gone (§4); every other part of it is unchanged. It is read with the grants part's + reading of memberships and rewritten at once with `mesh-grants.grants_compose_now`. 8. **A composition test holds the grant.** In `internal/buscompose/buscompose_test.go`, one table-driven test composes a runtime carrying the tool runner and one other module, and a runtime carrying the other module alone, and holds: the first's subscribe list contains `mesh.seat.*.event.>` and `mesh.mod.*.event.>`; no subscribe subject of either - is wider than those (none of `>`, `mesh.>`, `mesh.*.>`, `mesh.seat.>`, `mesh.mod.>`, nor any `mesh.stream.` or - `$JS.` subject beyond what the carried modules' own consumes give); the first's publish list holds no subject of - another module's or seat's events; the first derives no durable consumer beyond its carried modules' `consumes`; - and the second has neither subject (ADR 0670 §1, §3). + is wider than those (none of `>`, `mesh.>`, `mesh.*.>`, `mesh.seat.>`, `mesh.mod.>`, nor any `$JS.` subject beyond + what the carried modules' own consumes give); the first's publish list holds no subject of another module's or + seat's events; the first derives no durable consumer beyond its carried modules' `consumes`; and the second has + neither subject (ADR 0670 §1, §3). 9. **The real bus holds it too.** The grants part's existing real-bus test (`cmd/mesh-grants/composed_integration_test.go`) - gains three cases, no new test: the tool runner's account, as composed, subscribes + gains cases, no new test: the tool runner's account, as composed, subscribes `mesh.mod.mesh-delivery-view.event.delivered` and receives an event published there; it is refused a publish on - that subject; and another module's account is refused a subscription on an event it does not consume (ADR 0670 §1, - §3). + that subject; it is refused a subscription on `mesh.seat.channel.accept.show.` and on + `mesh.seat.intake.proof.code.` (an accept and a proof are never events, ADR 0670 §1); and another module's + account is refused a subscription on an event it does not consume (ADR 0670 §1, §3). ## The manifest validator, in the indexer @@ -100,83 +113,152 @@ test too, is rebased onto it. (`judge/rules_test.go`, the `waits` cases) are deleted, and one row is added: a manifest that carries `waits` is refused by the manifest validator as the checker runs it (`Shelf`), naming the unknown field `waits`, and set aside to wait as the indexer runs it (`ShelfWaiting`), as any field it does not know (ADR 0578 §1); the tool runner's - manifest without it is accepted - (ADR 0670 §2). + manifest without it is accepted (ADR 0670 §2). Only revisions taken in after the indexer's delivery are judged so: + a manifest the validator accepted before keeps its verdict (ADR 0578 §1). ## The tool runner (module `node-tools`) 11. **Its manifest loses `waits`.** `node-tools/module.json` drops the key `waits` and its five entries; nothing else in it changes, its `version` stays `2` (ADR 0670 §2). -12. **`mesh_wait` takes any event name.** `console.Surface.waitAnswer` (`internal/console/wait.go`) no longer reads the - membership: each name of `subjects` becomes its subjects through `buscompose.EventSubjects` (§1), a refused name is - answered as a failure quoting that error, and a call naming more than 16 names is refused, saying the bound (each - name is four subscriptions for the call; 16 names is 64, far above what a watcher names, and a call is never a - subscription to the whole bus). The same subject named twice is subscribed once. The map from subject to name goes: - the heard event is named by `buscompose.EventName` of the subject it arrived on. The timeout, the filter and the - timed-out answer are unchanged (ADR 0367 §1). The refusal of names outside a list, and its words naming the - manifest, go (ADR 0670 §2). -13. **The answer names the heard event.** A heard event is answered as today, `heard` with `subject`, `body` and +12. **The publication time is kept.** `bus.Heard` gains `Published`, read from the event's header `x-time`, which + mesh-sdk's emit sets (`go/process/emit.go`) and the status view's own emit too; `HearFirst` reads it from the + message's headers, and leaves it empty for an event that carries none. +13. **`mesh_wait` takes any event name.** `console.Surface.waitAnswer` (`internal/console/wait.go`) no longer reads the + membership. Each name of `subjects` becomes its subjects through `buscompose.EventSubjects` (§1); a refused name is + answered as a failure quoting that error. An optional argument `from`, `seat` or `module`, narrows every name to + that namespace, for an emitter whose name is both a seat's and a module's. A call naming more than 16 names is + refused, saying the bound (each name is four subscriptions for the call; 16 names is 64, far above what a watcher + names). The same subject named twice is subscribed once. The map from subject to name goes: the heard event is + named by `buscompose.EventName` of the subject it arrived on. The timeout, the filter and the timed-out answer are + unchanged (ADR 0367 §1). The refusal of names outside a list, and its words naming the manifest, go (ADR 0670 §2). +14. **A misspelt name is refused, not waited on for ever.** Before it subscribes, `waitAnswer` refuses a name whose + emitter is neither a seat of the seat list the tool runner reads through the seat `mesh-catalog` nor a module of + the indexer's manifests it holds as a first view (`mesh-delivery-indexer.views.manifests`), naming both sources. + A name whose emitter is known but whose event no seat definition and no manifest's `emits` declares is waited on, + and its timed-out answer says that no declaration names that event. While either source is not yet read (the + first views' state, `mesh_first_views`), names are not checked, and the answer says `unchecked` with why. +15. **The answer names the heard event.** A heard event is answered as today, `heard` with `subject`, `body` and `at`, where `subject` is now the heard event's whole name (`mesh-delivery-view.delivered`, - `operator-channel.decided.claude-code`), and a new field `from`, `seat` or `module`, says which namespace it arrived - on. The `read` line stays (an event only wakes you, ADR 0367 §4); for a name beginning `operator-channel.decided` - it says instead that the operator's answer is read only with `claude_code_answer`, never from the event's body - (ADR 0670 §5; ADR 0332 §5, §6). -14. **A refused subscription is said, and why.** `bus.Conn.HearFirst` is unchanged: it says a subscription the bus - refuses at once, naming the subject. `waitAnswer` answers that failure adding that the grants part grants this - machine's tool runner every event subject (ADR 0670 §1), so a refusal means its account's grant is not composed - yet, and that the state is read from its owner meanwhile. -15. **The tool's description says what it hears.** `waitTool`'s description of `subjects` says: one or more event + `operator-channel.decided.claude-code`), with two new fields: `from`, `seat` or `module`, the namespace it + arrived on, and `published`, the event's own time (§12), absent when it carries none. The `read` line stays (an + event only wakes you, ADR 0367 §4); for a name beginning `operator-channel.decided` it says instead that the + operator's answer is read only with `claude_code_answer`, never from the event's body (ADR 0670 §5; ADR 0332 §5, + §6). +16. **A refused subscription is said, and a wait is never a hot loop.** `HearFirst` subscribes every subject of the + call; a subscription the bus refuses is noted, and the others go on waiting. When every subscription of the call is + refused, it waits out the call's timeout (or the caller's going) and then answers the refusal, so a watcher that + loops on it asks once a timeout, never at once again. Its refusal words become: "the bus refused this account a + subscription on : the grants part grants this machine's tool runner every event subject (novox/hq ADR + 0670 §1), so its grant is not composed yet; read the state from its owner meanwhile"; no words name a wait grant or + ADR 0367 §2. A heard event's answer and a timed-out answer list under `refused` any subject the bus refused while + others stood. +17. **The tool's description says what it hears.** `waitTool`'s description of `subjects` says: one or more event names, `.`, such as `mesh-delivery-view.delivered`; an event whose subject goes on after - the name (an asker, a kind, a module) is heard by the name too; the filter narrows by the body's fields. Its own - description keeps "an event only wakes you" and adds that an operator's answer is read only with - `claude_code_answer`. The words "only those the mesh granted this machine's tool runner" go. -16. **The hearing serves `mesh_wait` alone.** `HearFirst` has one caller, `waitAnswer`; a carried module's code still - reaches the bus only as its own membership names it, as the runtime keeps it today (ADR 0670 §1). How it is - checked: the code review reads `HearFirst`'s callers. -17. **What goes from the tool runner's code.** The type `bus.WaitIssued` (`internal/bus/wait.go`) and `Membership.Waits` - (`internal/bus/bus.go`) go. `console/manifest_waits_test.go` keeps its test of what the tool runner consumes and - its test of the runner's own secrets; its test of the five families becomes a test that the manifest has no key - `waits`, and its comment cites ADR 0670 §2. `console/wait_test.go`'s `waitingRuntime` issues no waits, and its - tests wait on `mesh-delivery-view.delivered` and `operator-channel.decided.claude-code` with no membership naming - either; the test that a name outside the list is refused becomes a test that a name with `*`, `>` or one token is - refused (ADR 0670 §2). No test is added: the module stays within its budget. + the name (an asker, a kind, a module) is heard by the name too; the filter narrows by the body's fields; `from` + narrows to a seat's or a module's. Its own description keeps "an event only wakes you" and adds that an operator's + answer is read only with `claude_code_answer`. The words "only those the mesh granted this machine's tool runner" + go. +18. **The hearing serves `mesh_wait` alone.** `HearFirst` has one caller, `waitAnswer`. The modules the runtime carries + share its account, so the bus no longer bounds which events their code could hear; the runtime still gives a + carried module's code only what its membership names, and this is accepted under ADR 0670 §1, every session + already hearing every event. How it is checked: the code review of every change to the tool runner reads + `HearFirst`'s callers. +19. **What goes from the tool runner's code, and its tests.** The type `bus.WaitIssued` (`internal/bus/wait.go`) and + `Membership.Waits` (`internal/bus/bus.go`) go. In `console/manifest_waits_test.go` the test of the five families + becomes a test that the manifest has no key `waits`, its comment citing ADR 0670 §2; its tests of what the tool + runner consumes and of the runner's own secrets stay. In `console/wait_test.go`: `waitingRuntime` issues no waits, + and the answering test waits on `mesh-delivery-view.delivered` and on `operator-channel.decided` with no membership + naming either, its rows holding `subject`, `from`, `published` and `read` (a `decided.` event heard by + `operator-channel.decided`); `TestMeshWaitRefusesAnEventOutsideTheGrantedFamilies` becomes the table of refusals: + a name with `*`, with `>`, of one token, 17 names, an emitter no source knows (§14), and a bus-refused subscription + 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. No test function is added: the module stays within its + budget. + +## The claude-code module's Stop hook + +20. **The hook no longer doubts the grant.** In mesh-catalog `modules/claude-code/cmd/claude-code/stop.go`, + `watcherAdvice` says for a wait with an event: "mesh_wait on and reread after every answer, or start + a watcher that does", with no condition on the tool runner's grant; its comment cites ADR 0670 §2 beside ADR 0489 + §3. Its row in `stop_test.go` holds the new words. ## Events carry no secret -18. **The prepare lists every event.** The milestone file's prepare (playbook 07, step 0) gains a section *Events and +21. **The prepare lists every event.** The milestone file's prepare (playbook 07, step 0) gains a section *Events and their fields*: every event each module's manifest `emits` and each seat's definition declares, read from the - indexer's modules (`mesh-delivery-view.indexer_modules`) and the seat list (the seat `mesh-catalog`'s definitions), - each with the fields of its body as the emitting code builds it, by file and line (ADR 0670 §4). -19. **The code review confirms none carries a secret, before the grant is delivered.** The milestone's code review - (playbook 07, step 2) reads that list and confirms that no event's body carries a credential, a token that grants - an act, code, or a message marked secret; an intake `choice`'s one-use token is not such a token (ADR 0259 §3, §5). - An event found carrying one holds the milestone's delivery and goes to the operator as a design question - (ADR 0670 §4). -20. **From then on every change is asked the same.** Playbook 07's review step gains the question, for any change - that adds an event or a field to one: does it carry a secret? Written in this milestone's hq pull request + indexer's modules (`mesh-delivery-view.indexer_modules`) and the seat definitions (`mesh-catalog.seat-definitions`), + each a holder's event with its subject form; and every event mesh-sdk publishes on a module's behalf (the + announcement's `states` on `mesh.mod..event.states`, found by the callers of its subject builders); each + with the fields of its body as the emitting code builds it, by file and line (ADR 0670 §4). +22. **The code review confirms none carries a secret, before the grant is delivered.** The milestone's code review + (playbook 07, step 2) reads that list again at the group's heads, and confirms that no event's body carries a + credential, a token that grants an act, code, or a message marked secret; an intake `choice`'s one-use token is not + such a token (ADR 0259 §3, §5). A subject the bus holds that the list does not name is a finding of that review; how the subjects a stream holds + are read is open with the operator (no tool answers them today). + An event found carrying a secret holds the milestone's delivery and goes to the operator as a design question (ADR 0670 §4). +23. **The stream events are read too.** *Pending the operator's answer (stream events).* §21's list also names every + event kept in a module's own stream (`mesh.stream..event.`, ADR 0363 §2), the seat holders' events + published there among them, and whether its stream is restricted (ADR 0376 §1). +24. **From then on every change is asked the same.** In this milestone's hq pull request: playbook 07's review step + gains the question, for any change that adds an event or a field to one, "does it carry a secret?"; and the watcher + lines that describe the list (`.claude/agents/mesh-watcher.md`, playbook 07's *Following and reporting*: "the + families a watcher may wait on are those the mesh granted the tool runner") say instead that `mesh_wait` hears any + event by name (ADR 0670 §2, §4). + +## Stream events + +*Pending the operator's answer (stream events): every line of this section.* + +25. **The tool runner also hears the module streams, but no restricted one.** `buscompose.EveryEvent` (§1) gains a + third subject, `mesh.stream.*.event.>`; and the grants part's composition (§6) adds to the tool runner's runtime + account a subscribe deny of every restricted stream's subjects (`ModuleStream.Restricted`, ADR 0363 §4), which the + bus's deny outranks its allow for. `Permissions` gains `SubscribeDeny`, written into the user list as the server's + subscribe deny, as `PublishDeny` is for publishing (ADR 0376 §1 unchanged: the tool runner never hears a restricted + stream). +26. **A name hears its stream form.** `EventSubjects` (§1) answers six subjects, adding + `mesh.stream..event.` and the same followed by `.>`; `from` (§13) takes `stream` too; `EventName` + answers a stream subject with namespace `stream`. A seat's holder event kept in its holder's stream is named by its + holder module (`mesh-delivery-checker.verdict`). The bound of §13 stays 16 names (96 subscriptions). +27. **The tests hold it.** §8's test also holds the third subject and the deny of a fixture's restricted stream; §9's + real-bus test also holds that the tool runner's account hears a module stream's event and is refused a + subscription on a restricted stream's subject. ## Delivery -21. **One delivery group, no order named between its members.** The three repositories are pushed and checked as - one group. No order is named between them, and no transitional field is kept (the operator, 2026-11-03: a change - is designed as it should be, its delivery is the delivery system's): a part built with the new mesh-sdk reads a - stored manifest of the tool runner that still carries `waits` without it (ADR 0578 §3), and a node-engine is given a want - only for a manifest whose words it reads (ADR 0614 §4). Between the grants part's delivery and the tool runner's on a - node, `mesh_wait` there answers the bus's refusal (§14), or an older tool runner's "issued no event families": - both loudly, and a watcher reads its owner after every answer (ADR 0367 §4). +28. **One delivery group, no order named between its members.** The members are pushed and checked as one group. No + order is named between them, and no transitional field is kept (the operator, 2026-11-03: a change is designed as + it should be, its delivery is the delivery system's): a part built with the new mesh-sdk reads a stored manifest of + the tool runner that still carries `waits` without it (ADR 0578 §3), and a node-engine is given a want only for a + manifest whose words it reads (ADR 0614 §4). +29. **What `mesh_wait` answers between the deliveries.** On a node whose tool runner is new and whose grant is not yet + composed, the subjects the old grant names are still heard and the rest are listed under `refused` (§16); on a + node whose tool runner is older and whose membership the new grants part rewrote, the old runner answers at once + that it was issued no event families. Both are loud. A watcher reads its owner after every answer (ADR 0367 §4) and + ends at a call failed twice (its definition), so neither becomes a loop; a laptop asleep through the window gets + its new tool runner when it wakes, and its watchers read their owners meanwhile in the same way. +30. **Going back is going forward.** A fault met after the delivery is fixed by a new change delivered the same way. + Reverting the group instead reverts every member, the indexer first, since the manifest validator delivered here + sets aside the old tool runner's manifest carrying `waits` (§10); until the revert reaches a node, `mesh_wait` + there answers as §29 says. ## Acceptance -- **A watcher wakes on an event no manifest names** (the operator's acceptance): on the live mesh, a watcher's - `mesh_wait` on `mesh-delivery-view.delivered` answers the status view's `delivered` within a second of its - publication, with no rereading on a timer; the tool runner (`node-tools`) is delivered to every node, and the grants part and the - indexer to theirs, read with `mesh-delivery.show` and each node's `node-engine.composition` (§5, §12). -- **The grant is exactly two subjects**: the composition test of §8 and the real-bus cases of §9 pass. -- **The list is gone**: the manifest validator, as the checker runs it, refuses a manifest carrying `waits`, the +- **A watcher wakes on an event no manifest names** (the operator's acceptance; live, read by tool call): at the first + delivery after every node holds the new tool runner, a watcher's `mesh_wait` on `mesh-delivery-view.delivered` + answers the status view's `delivered`, its `at` less than a second after its `published`, with no rereading on a + timer; the tool runner (`node-tools`) is delivered to every node, and the grants part and the indexer to theirs, + read with `mesh-delivery.show` and each node's `node-engine.composition` (§6, §13, §15). +- **The grant is exactly what the record names** (unit, and integration on a real bus): the composition test of §8 + and the real-bus cases of §9 pass. +- **The list is gone** (unit): the manifest validator as the checker runs it refuses a manifest carrying `waits`, the indexer sets it aside, and the tool runner's manifest is accepted without it (§2, §10, §11). -- **No event carries a secret**: the milestone file's *Events and their fields* is complete and the code review's - confirmation is written in it before the grants part is delivered (§18, §19). -- **An answer is never taken from an event**: in the three repositories nothing reads an operator's answer from a - `decided` event's body, which the code review confirms, and `mesh_wait`'s answer for one says to read it with - `claude_code_answer` (§13). +- **`mesh_wait` refuses loudly and never loops** (unit): §19's table of refusals passes. +- **No event carries a secret** (the code review): the milestone file's *Events and their fields* is complete and the + code review's confirmation at the group's heads is written in it before the grants part is delivered (§21, §22). +- **An answer is never taken from an event** (the code review, and unit): in the members nothing reads an operator's + answer from a `decided` event's body, and `mesh_wait`'s answer for one says to read it with `claude_code_answer` + (§15). +- **A stream event wakes a watcher** (live, read by tool call). *Pending the operator's answer (stream events).* A + watcher's `mesh_wait` on `mesh-delivery-checker.verdict` wakes on the checker's next verdict (§25, §26). diff --git a/03-DESIGN/01-to-be/34-the-console.md b/03-DESIGN/01-to-be/34-the-console.md index 61f897fa..fb60566f 100644 --- a/03-DESIGN/01-to-be/34-the-console.md +++ b/03-DESIGN/01-to-be/34-the-console.md @@ -1,7 +1,7 @@ --- layer: to-be status: in-progress -code: [mesh-tools, mesh-host, mesh-controller, mesh-sdk, mesh-delivery] +code: [mesh-tools, mesh-host, mesh-controller, mesh-sdk, mesh-delivery, mesh-catalog modules/claude-code] updated: 2026-11-11 decisions: - "ADR 0670" @@ -9,6 +9,9 @@ decisions: - "ADR 0259" - "ADR 0332" - "ADR 0571" + - "ADR 0363" + - "ADR 0376" + - "ADR 0489" - "ADR 0578" - "ADR 0614" - "ADR 0197" @@ -148,7 +151,15 @@ of every seat and every module, and on nothing else of theirs: no accept, proof, publish. `mesh_wait` subscribes when called and closes the subscription when it answers, so the account keeps no durable consumer and no queue of its own (kept from ADR 0367 §2), and any event name is accepted: a new event is heard from its first publication, with no change to a manifest. The tool runner's manifest names no events, the -manifest validator has no rule about them, and no membership lists them. +manifest validator has no rule about them, and no membership lists them. A name whose emitter no seat and no module +bears is refused, so a misspelt wait does not run for ever. + +**Who hears.** Whoever may call the mesh MCP server: any process on the machine, through its loopback endpoint (§2), +which is the session there. The tool runner's standing grant of every event is accepted under ADR 0670 §1: what a +session hears through it, it may hear. + +*Pending the operator's answer (stream events):* the tool runner also hears the events kept in a module's own stream +(ADR 0363 §2), the seat holders' events published there among them, but never a restricted stream (ADR 0376 §1). **What that makes audible, and what still bounds it.** Every session now hears the operator channel's `decided` of every asker and the intake bench's events. So no event of a seat or a module carries a secret: no credential, no token @@ -217,7 +228,7 @@ matched it by URL. | the mesh MCP server | §1 to §5: the module, its loopback endpoint, its discovery (ADR 0152) | delivered 2026-09-30 (above) | | found by address | §3a: five announced tools and addresses (ADR 0195, ADR 0197) | delivered; [as-is 13](../00-as-is/13-the-console.md) | | waiting on the bus | §3b's `mesh_wait` on a list of five families in the tool runner's manifest (ADR 0367) | delivered (issue 745); [as-is 13](../00-as-is/13-the-console.md) | -| every-event | §3b as ADR 0670 changes it: the tool runner hears every event, and the list goes; one delivery group of mesh-sdk, mesh-delivery and mesh-tools | designed; its part is [`34-milestones/every-event.md`](34-milestones/every-event.md) | +| every-event | §3b as ADR 0670 changes it: the tool runner hears every event, and the list goes; one delivery group of mesh-sdk, mesh-delivery, mesh-tools and mesh-catalog, with hq | designed; its part is [`34-milestones/every-event.md`](34-milestones/every-event.md) | ## What this does not settle