From f6199fafcee4588ca43cbca24c884db4d25dc211 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 17 Oct 2026 13:39:14 +0200 Subject: [PATCH] As-is 13: fix five review findings on the mesh MCP server (issue 1008, review round 1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit node-tools still has a manifest (invokes *, waits, the mcp-endpoint provision) — describe it in prose instead of saying it has none. The TypeScript sources remain on main; only the built artifact is Go. The mesh-controller seat answers 63 verbs as its own runtime, not 60-93 per node (that is node-tools' own node-scoped seat verbs). Prefer reading mesh_overview live over a count that drifts. An as-is document amends nothing — reworded accordingly. --- 03-DESIGN/00-as-is/13-the-console.md | 67 ++++++++++++++++++---------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/03-DESIGN/00-as-is/13-the-console.md b/03-DESIGN/00-as-is/13-the-console.md index 44b19bf5..d9ee8a98 100644 --- a/03-DESIGN/00-as-is/13-the-console.md +++ b/03-DESIGN/00-as-is/13-the-console.md @@ -26,12 +26,20 @@ module of that name. **The tool runner is one of the mesh's core components**, named `node-tools` beside `mesh-controller`, `mesh-host` (the node-engine) and `nats` (the bus) — the mesh's own repositories' check treats the four the same way, as the core whose health the mesh itself defines rather than a -module's own. It is not a catalogue module: it has no manifest, is not assigned or unassigned, and -carries no `invokes` grant of its own in a manifest, because there is no manifest. Its code lives -under `node-tools/` in the `mesh-tools` repository (`internal/console`, `internal/announce`, -`internal/bus`), and `mesh-host` and `mesh-controller` supervise, compose and account for it as a -core process rather than as an assignment (`ComponentNodeTools`/`BusNodeTools` in both, the witness -and lease code that restarts and rolls it back, the account named `.node-tools` on the bus). +module's own. `mesh-host` and `mesh-controller` supervise, compose and account for it as a core +process rather than as an ordinary assignment (`ComponentNodeTools`/`BusNodeTools` in both, the +witness and lease code that restarts and rolls it back, the account named `.node-tools` on +the bus). + +**It still carries a manifest, `node-tools/module.json` in `mesh-tools`,** and that manifest is +where its grants live: `invokes` names `*`, so its account may call every tool on the mesh; +`waits` names the five event families `mesh_wait` may subscribe to (the knowledge base's reviews, +mesh-delivery's transitions, the forge's status, the controller's checked event, and the operator +channel's decision for the agent module) — the bus refuses a subscription outside this list +(ADR 0367 §2); and it +`provides` one provision, the MCP endpoint, at node scope, and `listens` on the port it serves from, +opened only to the machine itself. Its code lives under `node-tools/` in the `mesh-tools` +repository (`internal/console`, `internal/announce`, `internal/bus`). **One runtime per node, run by the node-engine as a sibling process, never a container** (ADR 0175). It holds the node's one bus credential and may call every tool on the mesh; its account @@ -48,9 +56,11 @@ body per request, no session and no event stream (`node-tools/internal/console/m **It announces seven tools, not five** (ADR 0195, extended by ADR 0367): `mesh_overview`, `mesh_machine`, `mesh_search`, `mesh_describe`, `mesh_call`, `mesh_runtimes` and `mesh_wait`. The -handshake's `instructions` text names all seven and the four address forms; measured live, the four -nodes' tool runners between them answer for 103 modules and 645 tools this way, with two module -instances (`route-proxy` on two of the nodes) named as not answering rather than silently absent. +handshake's `instructions` text names all seven and the four address forms. `mesh_overview` is +where the mesh-wide count of modules and tools is read live — a number copied into this document +would drift; asked on 2026-10-17 it answered 103 modules and 645 tools across the whole mesh, with +two module instances (`route-proxy` on two of the nodes) named as not answering rather than silently +absent. **Addresses, exactly as the handshake states them:** `.` for a seat held once for the mesh (`mesh-controller.status`); `/.` for a seat every machine holds @@ -74,7 +84,7 @@ answering in under 100ms for hundreds of kilobytes of discovery. **The seventh tool, added by ADR 0367**, lets a session wait on the bus for an event instead of polling: a call names one or more granted event families and an optional filter, and either answers the first match or says it timed out. The tool runner's own bus account hears only the families its -manifest-equivalent grant names; an event it hears is never taken as the state — a session reads the +manifest's `waits` list names; an event it hears is never taken as the state — a session reads the state from the verb that owns it once woken. It is meant to be called in a loop from a background command, not inline, because one call blocks until it answers or times out. @@ -82,32 +92,41 @@ command, not inline, because one call blocks until it answers or times out. **These already answer directly, not as a stated future.** Beside every assigned module's tools, the tool runner asks the `mesh-controller` seat for its own verbs the same way (ADR 0154): measured -live, that seat answers between 60 and 93 verbs per node (`status`, `push`, `assign`, `build`, -`artifacts` and the rest), and the `mesh-controller` *module* answers none of its own — it serves no -module tools, only its seat's, exactly as design 33 described while it was still only designed. +live, the mesh-controller runtime is one instance, mesh-wide, and it answers 63 verbs for that one +seat (`status`, `push`, `assign`, `build`, `artifacts` and the rest), and the `mesh-controller` +*module* answers none of its own — it serves no module tools, only its seat's, exactly as design 33 +described while it was still only designed. The 56-to-93 seat-verb counts each node's tool runner +answers (below) are a different thing: the tool runner serves the verbs of whichever node-scoped +seats are held on that node (`node-backup`, `node-build-agent`, `node-service-manager` among them), +on behalf of the modules holding them, not the mesh-controller seat's — that one is served by the +mesh-controller runtime itself, a separate process, and the tool runner only asks it and relays what +it answers. ## Where it runs **Every node, not a per-operator assignment.** Measured live: all four of the mesh's machines answer a `node-tools` runtime, each with its own module and tool count (between 294 and 312 tools, 56 to 93 seat verbs, in this reading). This is a fact that shipped past the design it came from: the -to-be document this amends still describes it as assigned to "whichever machines an operator sits -at"; what actually runs is the opposite — every node the mesh holds carries one, because it is core +to-be document still describes it as assigned to "whichever machines an operator sits at"; what +actually runs is the opposite — every node the mesh holds carries one, because it is core infrastructure the mesh itself depends on (discovery, the controller's own verbs, every module's tools), not a convenience assigned to a workstation. There is accordingly no assign or unassign verb for it: removing it from a node is not an operation the controller or the catalogue exposes. ## What this repository's prior text got wrong -The document this replaces described `mesh-console` as a catalogue module with an `invokes: ["*"]` -manifest, served by a container, assigned only to workstations, and implemented in TypeScript -(`mesh-tools src/mesh.ts`, `src/http.ts`, `src/runtime.ts`). None of that is true of what runs today: -the container, the manifest, the per-workstation assignment and the TypeScript implementation are -all gone, replaced in substance by ADR 0175's host-side, module-agnostic, Go runtime described -above. The quirks it recorded from the `mesh-console` era (a module registered by hand recording its -source as a URL; a *record*-policy module staying listed as not-answering until its rebuilt runtime -is pushed) are not re-checked here: they were about the `mesh-console` container's registration path, -which no longer exists. +The document this replaces described `mesh-console` as a catalogue module served by a container and +assigned only to workstations. That module and its container are gone; the manifest is not — it +moved to `node-tools/module.json`, still declares `invokes: ["*"]`, and gained `waits` and the +`mcp-endpoint` provision, described above. What also moved is which language builds it: the +manifest's `build.artifacts` names a Go binary built from `cmd/node-tools`, and that is what a node +runs. The repository still carries the earlier TypeScript sources (`node-tools/src/mesh.ts`, +`http.ts`, `runtime.ts`, `announce.ts`, `broker-nats.ts`, `client.ts`, `launch.ts`, `main.ts`, +`mcp.ts`) on main, but the manifest does not build from them. The quirks the prior text recorded +from the `mesh-console` era (a module registered by hand recording its source as a URL; a +*record*-policy module staying listed as not-answering until its rebuilt runtime is pushed) are not +re-checked here: they were about the `mesh-console` container's registration path, which no longer +exists. ## How it is checked