Files
hq/04-ISSUES/182-a-tool-call-reaches-whichever-instance-answers-first/00-report.md
T

51 lines
3.5 KiB
Markdown

---
status: resolved
opened: 2026-10-01
located-in: [mesh-tools src/broker-nats.ts (one subject, one queue group per module), mesh-tools src/mcp.ts (no way to name a machine), mesh-tools src/runtime.ts (a claimed seat's verbs served by nobody), mesh-controller internal/broker/nats.go (the grant for a tool named one subject)]
fixed-by: mesh-tools PR (feat/a-tool-call-names-the-machine) and mesh-controller PR (same branch) — see ADR 0159; the store seat's verbs and postgres's tools follow in the catalogue
amended-design: [03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md, 03-DESIGN/01-to-be/34-the-console.md]
---
# 182 — A tool call reaches whichever instance answers first, and a claimed seat's verbs are served by nobody
## What was observed
Asked how to list the databases of the store on one machine, the mesh had no answer. A module's tools
are served on one subject per module, `mesh.mod.<module>.tool.<name>`, and every instance of the
module joins one queue group on it, so a call to the database engine's tool while it runs on two
machines reaches whichever answered first, and the answer does not say which. There is no way to ask
the instance on one machine. The console lists the tool once and offers no machine.
And the seat half was missing too. Design 33 §3 says holding a seat means serving its tools, and
ADR 0154 built that for the controller's own seat alone. A module that holds a seat — the database
engine on the control node holding `mesh-store` — served none of the seat's verbs, because no seat
but the controller's declares any and no runtime knew which seats its module claimed.
## Why this is here
Both are the same omission: the tool surface was built as if every module ran on one machine and
held no seat. A queue group is the right default for a stateless module answering anywhere, and the
wrong only choice for a module whose instances are different things — two stores with different
databases. The architecture had the distinction: a module is a thing that runs on machines, a seat is
a role one of them holds. The tool surface did not carry it.
## Resolved, 2026-10-01
[ADR 0159](../../02-DECISIONS/0159-a-tool-call-names-the-machine-and-a-holder-serves-its-seats-verbs.md).
Every instance serves its module's subject twice: in the queue group as before, and with its own
machine as the subject's last token. `<module>.<tool>@<node>` reaches one machine's instance; the
console lists `node` on every module tool and puts it in the subject, never in the module's
arguments; every answer carries the machine that gave it, and the console appends it as its own line.
The grant for a tool covers both subjects.
A holder's runtime serves its seat's verbs: the credential the mesh writes names the seats the module
claims and the verbs each promises, the runtime serves each verb with the module's tool of the same
name on the seat's own subject, flat for a mesh seat and with the machine for a node-scoped one, and
the bus admits the subscription only where the module holds the seat. The store seat's first verbs
and the database engine's tools for them are the catalogue's next step, recorded in the decision.
*How it is checked:* against a real bus, a module on two machines answers each by name and says who
answered when unnamed, and a claimant answers a seat's verb on the seat's subject; the console lists
`node` on a module's tool and not on a seat's; the grant for a tool covers both subjects; and, live,
the store's databases listed from one named machine through the console.