A manifest the control plane can open, and stop swallowing the failure when it cannot

mesh-control runs in a container, so a manifest pushed to the machine is not a
file it can read; `module add` said so plainly and it was briefly taken for a
missing manifest. It is copied the last step of the way now.

The base's registration was doing this too, and its failure was swallowed by a
bare catch on the reasoning that the module might already be known. The step
passed regardless — a base with nothing to stand on builds whether or not the
mesh holds a record of it — and the fault surfaced one step later, where the
record was needed.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
2026-09-14 20:46:11 +02:00
parent babf08b9f8
commit 4ef0a19053
+24 -8
View File
@@ -138,6 +138,22 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi
async function mesh(command: string, timeoutMs?: number): Promise<string> { async function mesh(command: string, timeoutMs?: number): Promise<string> {
return must(CONTROL, `docker exec mesh-control /mesh-control ${command}`, timeoutMs); return must(CONTROL, `docker exec mesh-control /mesh-control ${command}`, timeoutMs);
} }
/**
* Register a module from a manifest on this workstation.
*
* **The control plane runs in a container, so a file on the machine is not a file it can open.**
* Pushing the manifest to the machine and naming that path got `no such file or directory` from
* inside mesh-control, which is correct and was briefly mistaken for a missing manifest. It is
* copied the last step of the way with `docker cp`.
*/
async function registerModule(module: string, manifest: string): Promise<string> {
assert.ok(existsSync(manifest), `no manifest for ${module} at ${manifest}`);
const onMachine = `/tmp/${module}.json`;
await push(instanceId, CONTROL, manifest, onMachine);
await must(CONTROL, `docker cp ${onMachine} mesh-control:${onMachine}`);
return mesh(`module add ${onMachine}`);
}
function tokenFrom(said: string): string { function tokenFrom(said: string): string {
const found = said.split("\n").map((l) => l.trim()).find((l) => l.length > 100 && !l.includes(" ")); const found = said.split("\n").map((l) => l.trim()).find((l) => l.length > 100 && !l.includes(" "));
assert.ok(found, `no token in:\n${said}`); assert.ok(found, `no token in:\n${said}`);
@@ -250,12 +266,15 @@ before(async () => {
// machine, published into the mesh's own registry. // machine, published into the mesh's own registry.
await step("the mesh builds the shared base from source", await step("the mesh builds the shared base from source",
"three machines join it across the household gateway", async () => { "three machines join it across the household gateway", async () => {
assert.ok(existsSync(baseManifest),
`no manifest for the shared base at ${baseManifest} — set MESH_LAB_BASE_MANIFEST`);
await push(instanceId, CONTROL, baseManifest, `/tmp/${BASE.module}.json`);
// Registered from the manifest the builder will also read, so what the mesh holds and what it // Registered from the manifest the builder will also read, so what the mesh holds and what it
// builds are the same description of the same module. // builds are the same description of the same module.
await mesh(`module add /tmp/${BASE.module}.json`).catch(() => {}); //
// **Not swallowed.** This call used to end in `.catch(() => {})`, on the reasoning that the
// base might already be known. It hid a real failure — the manifest was being named at a path
// inside a container that had never seen it — and the step passed anyway, because a base with
// nothing to stand on builds whether or not the mesh has a record of it. The next step, which
// needs that record, is where it surfaced.
await registerModule(BASE.module, baseManifest);
const built = await mesh( const built = await mesh(
`build ${forgeUrl(BASE.repo)} --ref ${buildRef} --wait 1200s`, 1_500_000); `build ${forgeUrl(BASE.repo)} --ref ${buildRef} --wait 1200s`, 1_500_000);
assert.doesNotMatch(built, /failed/i, built); assert.doesNotMatch(built, /failed/i, built);
@@ -265,10 +284,7 @@ before(async () => {
// ---- 4. AND A MODULE STANDING ON IT --------------------------------------------------------- // ---- 4. AND A MODULE STANDING ON IT ---------------------------------------------------------
await step("the mesh builds a module standing on that base", await step("the mesh builds a module standing on that base",
"the mesh builds the shared base from source", async () => { "the mesh builds the shared base from source", async () => {
const manifest = resolve(catalogDir, MODULE.module, "module.json"); await registerModule(MODULE.module, resolve(catalogDir, MODULE.module, "module.json"));
assert.ok(existsSync(manifest), `no manifest at ${manifest}`);
await push(instanceId, CONTROL, manifest, `/tmp/${MODULE.module}.json`);
await mesh(`module add /tmp/${MODULE.module}.json`);
const built = await mesh( const built = await mesh(
`build ${forgeUrl(MODULE.repo)} --path ${MODULE.path} --ref ${buildRef} --wait 1200s`, `build ${forgeUrl(MODULE.repo)} --path ${MODULE.path} --ref ${buildRef} --wait 1200s`,
1_500_000); 1_500_000);