The bed keeps its genesis warm

MESH_LAB_WARM=1 snapshots the post-genesis, both-nodes-enrolled state and restores it in
seconds on later runs — refused, not silently rebuilt, when the commits have moved
(src/warm.ts, the mechanism mesh.test.ts already uses and this session had ignored).
Fresh stays the default.

https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
2026-09-17 22:55:00 +02:00
parent 832c287ccc
commit 5ded8e2a8e
@@ -34,6 +34,7 @@ import { destroy, exec, push } from "../../src/lifecycle/operate.ts";
import { bootstrapBinaryPath, hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts";
import { labIsUsable, destroyAll, foundationBundle } from "./harness.ts";
import { genesis, type GenesisResult } from "./genesis.ts";
import { keep, ready, returnTo } from "../../src/warm.ts";
const SCENARIO = "built-store-cross-node";
const CONTROL = "anchor";
@@ -50,6 +51,13 @@ const catalogDir = process.env["MESH_LAB_CATALOG"] ?? "";
const source = process.env["MESH_LAB_SOURCE"] ?? "";
const sourceRef = process.env["MESH_LAB_SOURCE_REF"] ?? "";
const KEEP = !!process.env["MESH_LAB_KEEP"];
/**
* MESH_LAB_WARM=1: restore the post-genesis, both-nodes-enrolled state from a snapshot instead of
* re-running the installer (minutes -> seconds), refused — not silently rebuilt — when the commits
* it was built from have moved (src/warm.ts). Fresh stays the default: a run that must MEAN
* something raises from nothing.
*/
const warming = process.env["MESH_LAB_WARM"] === "1";
function forgeUrl(repo: string): string {
const override = process.env[`MESH_LAB_SOURCE_${repo.toUpperCase().replaceAll("-", "_")}`];
@@ -151,6 +159,21 @@ async function waitForContainer(node: string, container: string, seconds = 300):
before(async () => {
if (skip) return;
if (warming) {
const said = await ready(SCENARIO);
if (said.use === "restore") {
instanceId = said.instanceId;
const seconds = await returnTo(instanceId);
// A snapshot captures disk, not memory: the restore reboots. anchor's host is a systemd
// service (genesis hostService) and comes back on its own; node2's was hand-started.
await must(NODE, `pgrep -x mesh-host >/dev/null || (nohup ${HOST_PATH} run > /var/log/mesh-host.log 2>&1 & sleep 3)`);
const back = (await on(CONTROL, `docker ps --format '{{.Names}}'`)).out;
assert.match(back, /mesh-controller/, `the control plane did not come back after restore:\n${back}`);
console.log(`warm: returned ${instanceId} to its post-genesis state in ${seconds.toFixed(1)}s`);
return;
}
console.log(`warm: raising fresh — ${said.why}`);
}
const bed = await raise(loadScenario(`scenarios/${SCENARIO}.yml`), {
onProgress: (m) => console.log(`raise: ${m}`),
});
@@ -188,9 +211,17 @@ before(async () => {
const said = await must(NODE, `${HOST_PATH} enrol --token ${quote(token)}`);
assert.match(said, new RegExp(`enrolled as ${NODE}`), said);
await must(NODE, `nohup ${HOST_PATH} run > /var/log/mesh-host.log 2>&1 & sleep 3`);
// The expensive, deterministic part is done: genesis ran and both machines are enrolled. Keep it.
if (warming) {
const warm = await keep(SCENARIO, instanceId);
console.log(`warm: ${warm.instanceId} kept, against ` +
Object.entries(warm.against).map(([n, c]) => `${n} ${c}`).join(", "));
}
}, { timeout: 7_200_000 });
after(async () => {
if (warming) { console.log(`warm — ${instanceId} stays; \`mesh-lab warm cool\` retires it`); return; }
if (KEEP) { console.log(`MESH_LAB_KEEP set — leaving ${instanceId} standing`); return; }
if (instanceId) await destroy(instanceId);
await destroyAll(`${SCENARIO}-`);