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

5.0 KiB

status, opened, located-in, fixed-by, replay-none, amended-design
status opened located-in fixed-by replay-none amended-design
resolved 2026-10-05
mesh-tools
mesh-controller
novox/mesh-controller PR 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

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.

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.