Files
hq/04-ISSUES/244-a-verb-whose-schema-is-empty-cannot-be-called-through-the-console/00-report.md
T
jochen 2d18fa3380
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Triage issues 228-278: close the fixed, re-check the rest
Each issue is resolved with its fix and a replay or the reason none exists, or
re-checked against main and the live mesh on 2026-10-08.
2026-10-08 01:31:10 +02:00

97 lines
5.0 KiB
Markdown

---
status: resolved
opened: 2026-10-05
located-in: [mesh-tools, mesh-controller]
fixed-by: novox/mesh-controller PR #71 (ede9bce), novox/mesh-tools PR #14 (9730bd8)
replay-none: opened and fixed before ADR 0237 made a replay a condition of resolving; the fix's own tests, named above, guard it, but none was laid over the commit before the fix and registered, and doing so now would be a new test written after the fact rather than a replay; the controller's schema tests walk every verb it serves, so a verb added later fails them
amended-design:
---
# 244 — A verb whose published schema is empty cannot be called through the console, and says only that it needs the argument it will not take
## What was observed
Through the console, 2026-10-05, composing one machine's declaration before pushing it:
```
mesh_describe mesh-controller.plan
→ {"properties": {}, "type": "object"} ... it takes nothing
mesh_call mesh-controller.plan {}
→ plan failed: plan needs "node" ... it takes something
mesh_call mesh-controller.plan {"node": "novox"}
→ plan failed: plan needs "node" ... and not that
```
`mesh-controller.node` describes the same way and behaves the same way. Both are listed in the
node's own instructions as the mesh's verbs, so they are the first thing a session reaches for.
The verb is not broken where it runs: the same `plan novox --json` through the machine's login
shell answers in full. What cannot be done is calling it **through the console**, which the
node's instructions say is the only way to the mesh.
## Why
Not diagnosed past the symptom, and the symptom is specific enough to place it: the published
schema declares no properties, and an argument that is not in the schema does not reach the verb.
So the verb reports a missing argument that the console has dropped, every time, whatever is
sent. The two halves are each defensible — publish a schema, honour it — and together they make
a verb that can only refuse.
## Why it matters beyond this instance
**A tool that cannot be called is worse than one that is absent**, because it is listed. It
appears in `mesh_overview`, it describes itself, and the only thing it will say is that it wants
something it will not accept. A reader has no way to tell that from their own mistake, and the
obvious next move — pass the argument it names — is the one that does not work.
It also marks what the console does not check. Nothing compares a verb's published schema against
the arguments the verb actually requires, so a verb can be published in a shape that makes it
uncallable and nothing says so. That is the shape of an unenforced rule: the schema is believed,
and it is wrong.
## What a fix has to settle
- Where the schema for a seat's verbs comes from, and why these two publish an empty one while
the verbs beside them (`status`, `builds`, `push`) take their arguments and answer.
- Whether the console should refuse to publish a verb whose schema cannot satisfy it, rather than
listing one that can only fail.
- **How it is checked:** every verb the console lists is callable with the arguments its own
schema describes — a test that calls each with its schema's required set and asserts the answer
is not "needs" an argument the schema does not have.
## Seen again, 2026-10-05 and 2026-10-06 — and once it did harm
The same picture for more of the mesh's own verbs, read through the console:
```
mesh_describe mesh-controller.push → {"properties": {}}
mesh_describe mesh-controller.plan → {"properties": {}}
mesh_describe mesh-controller.assign → module only, no node
mesh_describe mesh-controller.pin → provision, from, module — no node
mesh_describe mesh-controller.settings → module, values, clear — no node
```
`build` and `queue`, which take no machine, described in full. For `plan` this is the refusal above.
For **`push` it was worse than a refusal**: a push takes no required argument, so a call naming one
machine arrived empty, ran as a push of every machine behind, and answered "N node(s) told" — an
operator who believed they had pushed one machine had pushed the mesh. Nothing in the answer said the
machine had been lost.
The diagnosis is in [`01-diagnosis.md`](01-diagnosis.md).
## Worked around, for now
Through `<node>/node-login-shell.execute`, running the controller's own command line on the
machine. That is the path the console exists to replace, so it is a workaround and not an answer.
## Resolved — 2026-10-08
Both pull requests named above are merged and live. Read through the console on 2026-10-08:
`mesh_describe mesh-controller.plan` declares `node` (required) and `files`; `mesh-controller.push`
declares `node`, `behind`, `cause` and `why`, and its description says that a push naming no machine
is a push of the whole mesh. The controller's `seatverbs_schema_test.go` walks every verb it serves:
an argument a verb does not declare is refused, and every flag of a verb's command is in its schema
or accounted for.