62 lines
3.7 KiB
Markdown
62 lines
3.7 KiB
Markdown
---
|
|
status: open
|
|
opened: 2026-10-04
|
|
located-in: []
|
|
fixed-by:
|
|
amended-design:
|
|
---
|
|
|
|
# 229 — A rollout cannot be followed through the mesh's tools, so an agent goes round them
|
|
|
|
## What was observed
|
|
|
|
2026-10-04, rolling out to-be 41. An agent drove the rollout through the mesh's MCP tools: the
|
|
controller seat's `status`, `plans`, `command`, and the forge's merge. Four times it left those tools
|
|
and posted JSON-RPC by hand to the node console's HTTP endpoint with `curl`:
|
|
|
|
1. **To wait for a plan.** `plans` answers once, with prose. Nothing waits for a plan to reach a tier,
|
|
finish or fail. An agent's tools cannot be called from a shell loop, so the only way to be told
|
|
when a plan moved was a background `curl` loop polling the console every twenty seconds and
|
|
matching the plan's line with `grep`.
|
|
2. **To read `status`.** `status` answers a paragraph of prose (the bus's user list), then a JSON
|
|
document, both inside one string. Picking out `behind`, `waiting` and `reported` took a script
|
|
that cut the string at the first brace and parsed the rest.
|
|
3. **To read one module out of `module list`,** whose output was too long to read whole for one line.
|
|
4. **To call a tool that arrived after the agent's session began.** The modules rolled out in that same
|
|
session added `node-login-shell.execute` and `zsh.zsh_config` to one machine. The agent's MCP
|
|
connection had been opened before the console moved to discovery ([ADR 0195](../../02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md)).
|
|
It still held the flat catalogue the console announced then, which lacks both the new verbs and the five discovery tools (`mesh_call` among
|
|
them) the console announces now. Clearing a session does not reconnect its MCP servers, and the
|
|
console never sends a list-changed notice, so nothing told the client its list was stale. The agent
|
|
posted `mesh_machine` and `mesh_call` by hand. Reconnecting the server would have given it the
|
|
discovery tools, which reach any tool by address the moment it exists.
|
|
|
|
The calls were authorised, because the console is the operator's own surface. But each is a raw call
|
|
the mesh's tools were meant to make unnecessary ([ADR 0154](../../02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md)
|
|
puts the controller's verbs behind the seat). Each is also a script that breaks silently when a
|
|
sentence in the prose changes.
|
|
|
|
## Why it matters beyond this instance
|
|
|
|
Every rollout an agent drives has the same shape: merge, wait for a plan, push, wait for reports,
|
|
check `status`. When the tools answer only once and only in prose, every agent writes its own poller
|
|
and its own parser. Those are invisible to review, different each time, and wrong the first time the
|
|
wording moves. An agent that cannot wait also tends to act early, which is the opposite of what a
|
|
rollout needs.
|
|
|
|
## What a fix has to settle
|
|
|
|
- A way to **wait** on the mesh's own progress. For example, `plans` and `status` could take a plan
|
|
or node and a bound, and answer when it moves or the bound passes. Or a verb could follow one plan
|
|
to its end.
|
|
- **Structured answers** from the controller's verbs, with the prose as a field beside the data, not
|
|
around it.
|
|
- Whether `command`'s generic answer should take a filter, or whether the verbs it is used for most
|
|
(`module list`, `node show`) deserve verbs of their own.
|
|
- **A client is told when the console's own surface changes.** The console announces `listChanged`
|
|
and sends the notice when what it lists changes, for example after an upgrade that changes its
|
|
tools. A long-running session then never keeps a list the console no longer serves. Discovery
|
|
already makes every module's tools reachable without the list changing.
|
|
|
|
How each is checked belongs to the record that settles it.
|