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.
97 lines
5.0 KiB
Markdown
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.
|