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
This commit is contained in:
@@ -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 `<emitter>.<event>`, optionally followed
|
||||
by further tokens (`operator-channel.decided.claude-code`); each token matches `[A-Za-z0-9_-]+`. It answers four
|
||||
subjects: `mesh.seat.<emitter>.event.<rest>`, the same followed by `.>`, `mesh.mod.<emitter>.event.<rest>`, and
|
||||
the same followed by `.>`, where `<rest>` 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 `<rest>` 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.<emitter>.event.<rest>` or
|
||||
`mesh.mod.<emitter>.event.<rest>` it answers the name `<emitter>.<rest>` and whether the emitter is a seat; any
|
||||
other subject is answered as not an event.
|
||||
`mesh.mod.<emitter>.event.<rest>` it answers the name `<emitter>.<rest>` 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>.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>.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.<kind>` and on
|
||||
`mesh.seat.intake.proof.code.<kind>` (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 <subjects>: 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, `<seat or module>.<event>`, 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.<asker>` 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 <event> and reread <read> 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.<module>.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.<module>.event.<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.<emitter>.event.<rest>` 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).
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user