A run-once step leaves nothing to ask, and V4 asked anyway

V4's first run reported a working mesh as broken: it asserted that lavinmq's
run-once bootstrap container existed, and the host removes an exited run-once
container on purpose — so a later apply is not confused by a stopped one, keeping
the record that it ran in its own store instead.

So the check asserted the opposite of correct behaviour. The step had run; it is
why the broker came up configured.

Counted as unverified now rather than assumed good. What would verify a step is
the host's own record of having run it, and this walks the machine rather than
the host — so the honest answer is that this check says nothing about steps, and
it now says so.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
2026-09-15 01:35:42 +02:00
parent 1425f5e7d4
commit fce554d150
2 changed files with 34 additions and 21 deletions
+24 -17
View File
@@ -1,13 +1,13 @@
{ {
"scenario": "one-node-mesh", "scenario": "one-node-mesh",
"established": 20, "established": 19,
"of": 21, "of": 22,
"steps": [ "steps": [
{ {
"code": "R1", "code": "R1",
"title": "a bare machine becomes a mesh of one, raised by the installer", "title": "a bare machine becomes a mesh of one, raised by the installer",
"status": "pass", "status": "pass",
"seconds": 145, "seconds": 140,
"why": "" "why": ""
}, },
{ {
@@ -56,21 +56,21 @@
"code": "P1", "code": "P1",
"title": "the mesh builds the shared base from source", "title": "the mesh builds the shared base from source",
"status": "pass", "status": "pass",
"seconds": 72, "seconds": 87,
"why": "" "why": ""
}, },
{ {
"code": "P2", "code": "P2",
"title": "the mesh builds and runs a store of its own", "title": "the mesh builds and runs a store of its own",
"status": "pass", "status": "pass",
"seconds": 43, "seconds": 39,
"why": "" "why": ""
}, },
{ {
"code": "P3", "code": "P3",
"title": "the mesh builds and runs its own catalogue", "title": "the mesh builds and runs its own catalogue",
"status": "pass", "status": "pass",
"seconds": 26, "seconds": 32,
"why": "" "why": ""
}, },
{ {
@@ -105,14 +105,14 @@
"code": "U2", "code": "U2",
"title": "the mesh runs a broker for that module to talk to", "title": "the mesh runs a broker for that module to talk to",
"status": "pass", "status": "pass",
"seconds": 20, "seconds": 31,
"why": "" "why": ""
}, },
{ {
"code": "U3", "code": "U3",
"title": "the anchor runs the module the mesh built", "title": "the anchor runs the module the mesh built",
"status": "pass", "status": "pass",
"seconds": 6, "seconds": 7,
"why": "" "why": ""
}, },
{ {
@@ -125,9 +125,9 @@
{ {
"code": "V2", "code": "V2",
"title": "the catalogue holds every module this mesh built", "title": "the catalogue holds every module this mesh built",
"status": "fail", "status": "pass",
"seconds": 1, "seconds": 3,
"why": "the catalogue does not hold mesh-tools, postgres — the mesh built them and its own record has no trace of it (novox/hq issue 050):\n{\"modules\":[{\"module\":\"amqp-ping\",\"commit\":\"e0c92195d4240841bfcf4b4a9ef869d5afeca331\",\"repository\":\"https://git.novox.be/novox/mesh-catalog.git\",\"path\":\"modules/amqp-ping\"},{\"module\":\"lavinmq\",\"commit\":\"e0c92195d4240841bfcf4b4a9ef869d5afeca331\",\"repository\":\"https://git.novox.be/novox/mesh-catalog.git\",\"path\":\"modules/lavinmq\"},{\"module\":\"mesh-control\",\"commit\":\"5062c36fc9efe159aa9706c0ca2c873351ef1ce0\",\"repository\":\"https://git.novox.be/novox/mesh-control.git\",\"path\":\"\"}]}\n\n+ actual - expected\n\n+ [\n+ 'mesh-tools',\n+ 'postgres'\n+ ]\n- []\n" "why": ""
}, },
{ {
"code": "V3", "code": "V3",
@@ -136,19 +136,26 @@
"seconds": 1, "seconds": 1,
"why": "" "why": ""
}, },
{
"code": "V4",
"title": "every resource the mesh declared is true on the machine",
"status": "fail",
"seconds": 13,
"why": "the machine is not what the mesh said it should be:\n container lavinmq.bootstrap: the container lavinmq-bootstrap was never created\n+ actual - expected\n\n+ [\n+ 'container lavinmq.bootstrap: the container lavinmq-bootstrap was never created'\n+ ]\n- []\n"
},
{ {
"code": "E1", "code": "E1",
"title": "a change to a module's source reaches the machine on its own", "title": "a change to a module's source reaches the machine on its own",
"status": "pass", "status": "skip",
"seconds": 14, "seconds": 0,
"why": "" "why": "not attempted — V4 (every resource the mesh declared is true on the machine) did not succeed"
}, },
{ {
"code": "E2", "code": "E2",
"title": "the mesh comes back after the machine reboots", "title": "the mesh comes back after the machine reboots",
"status": "pass", "status": "skip",
"seconds": 32, "seconds": 0,
"why": "" "why": "not attempted — E1 (a change to a module's source reaches the machine on its own) did not succeed"
} }
] ]
} }
+10 -4
View File
@@ -893,10 +893,16 @@ before(async () => {
case "container": { case "container": {
const name = String(r["name"]); const name = String(r["name"]);
if (r["run-once"] === true || r["schedule"]) { if (r["run-once"] === true || r["schedule"]) {
// Not expected to be running: it ran, or it runs later. What matters is that it exists // **A run-once step leaves nothing to ask, deliberately.** The host removes the exited
// and, if it ran, that it succeeded. // container so a later apply is not confused by a stopped one, and keeps the record
await check(`docker inspect ${quote(name)} >/dev/null 2>&1`, // that it ran in its own store instead. So asserting the container exists asserts the
`the container ${name} was never created`); // opposite of correct behaviour — which this did, and reported a working mesh as
// broken on its first run.
//
// A scheduled step is the same between fires. Both are counted as unverified here
// rather than assumed good: what would verify them is the host's own record, and this
// asks the machine rather than the host.
unchecked.push(`${kind} ${id} (a step leaves nothing running to ask)`);
} else { } else {
await check(`docker ps --format '{{.Names}}' | grep -qx ${quote(name)}`, await check(`docker ps --format '{{.Names}}' | grep -qx ${quote(name)}`,
`the container ${name} is not running`); `the container ${name} is not running`);