diff --git a/03-DESIGN/01-to-be/25-the-bus-on-nats.md b/03-DESIGN/01-to-be/25-the-bus-on-nats.md index 61bdb53..7ecfe38 100644 --- a/03-DESIGN/01-to-be/25-the-bus-on-nats.md +++ b/03-DESIGN/01-to-be/25-the-bus-on-nats.md @@ -56,11 +56,20 @@ mesh.control.enrol an enrolment request (JetStream: CONTR mesh.control.built a build's outcome (JetStream: CONTROL) mesh.node..declare a declaration for a node (JetStream: NODES, last-per-subject) mesh.build.request work for the build machine (JetStream: BUILDS, work queue) -mesh.events.. an event (JetStream: EVENTS) -mesh.tools.. a tool invocation (core request/reply) +mesh.mod..event. an event (JetStream: EVENTS) +mesh.mod..tool. a tool invocation (core request/reply) +mesh.seat..accept. work submitted to a role (JetStream: per-seat work queue) +mesh.seat..event. a role's own event (JetStream: EVENTS) +mesh.seat..tool. a role's tool (core request/reply) mesh.ask.. the controller's command api (core request/reply) ``` +**Revised 2026-09-26** ([design 29](29-what-a-module-declares.md)): a module's events and tools +moved from `mesh.events.*` / `mesh.tools.*` into one namespace per module, `mesh.mod..>`, +so a module's authority over its own name is a single subject pattern the server enforces — and +each carries a **kind token**, without which an events stream's filter would capture tool calls. +Seats are the same shape, one namespace per role. + Two things this buys over the exchanges: **request/reply is native** — a tool call is one `request` on `mesh.tools..` answered by whichever runtime serves it (a queue group per tool, so several nodes may serve one tool); and **a declaration is last-per-subject** — the NODES @@ -93,7 +102,7 @@ Core NATS is at-most-once. Everything the mesh must not lose lives in a JetStrea | CONTROL | `mesh.control.>` except `alive` | work queue, one consumer (the controller), explicit ack | the store-window guarantee ([ADR 0083](../../02-DECISIONS/0083-one-push-leaves-the-mesh-consistent.md)): the controller `nak`s with a delay while its store is away and the message is redelivered; nothing is dropped | | NODES | `mesh.node.>` | last per subject | one declaration per node, always the newest | | BUILDS | `mesh.build.>` | work queue, explicit ack | at least once; a builder that dies mid-build has its message redelivered | -| EVENTS | `mesh.events.>` | limits (age, size), durable consumer per subscribing module | a subscriber that was down catches up; after `max-deliver` attempts the advisory feeds `mesh.events.dead` (its own small stream) | +| EVENTS | `mesh.mod.*.event.>` | limits (age, size), durable consumer per subscribing module | a subscriber that was down catches up; after `max-deliver` attempts the advisory feeds `mesh.events.dead` (its own small stream) | Tool calls and heartbeats stay on core NATS: a lost heartbeat is the next heartbeat; a lost tool call is a timeout the caller already handles. diff --git a/03-DESIGN/01-to-be/29-what-a-module-declares.md b/03-DESIGN/01-to-be/29-what-a-module-declares.md index 72bcaa4..7349d78 100644 --- a/03-DESIGN/01-to-be/29-what-a-module-declares.md +++ b/03-DESIGN/01-to-be/29-what-a-module-declares.md @@ -34,11 +34,19 @@ the catalogue, and the mesh would have hundreds of copies of a decision it made | declared | derived | |---|---| -| `emits: order.placed` | publish on `mesh.mod..order.placed` | -| `consumes: billing.order.placed` | durable consumer on `mesh.mod.billing.order.placed` | +| `emits: order.placed` | publish on `mesh.mod..event.order.placed` | +| `consumes: billing.order.placed` | durable consumer on `mesh.mod.billing.event.order.placed` | | `serves: status` | queue-group subscription on `mesh.mod..tool.status` | -| seat `telegram-sender`, `accepts: send` | work-queue consumer on `mesh.seat.telegram-sender.send` | -| `uses: telegram-sender` | publish on that seat's `accepts` subjects, and nothing else | +| seat `telegram-sender`, `accepts: send` | work-queue consumer on `mesh.seat.telegram-sender.accept.send` | +| `uses: telegram-sender` | publish on that seat's `accept` subjects, and nothing else | + +**The `event` / `tool` / `accept` token is load-bearing, not decoration.** Revision, found while +defining the streams: a stream is defined by a subject filter, so a namespace holding both a +module's events and its tool calls cannot be filtered into an events stream without capturing +every tool invocation in the mesh — and a tool call must never be persisted +([design 25](25-the-bus-on-nats.md) §3 keeps tools on core NATS, where a lost call is a timeout the +caller already handles). The kind token is what makes `mesh.mod.*.event.>` a safe filter. The +first draft of this table had no token, which reads better and cannot be implemented. **The test this must pass: the manifest survives the wire changing.** Reorganise the subject space and every manifest in the catalogue is still correct. That is the property