From 3d5fac58efe83ea8f5cd492a2501e1f381b6e054 Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Wed, 7 Oct 2026 09:13:13 +0000 Subject: [PATCH] class v5 harness: the previous run's survivors (two nodes of the known-failed case were still bound at 08:49Z and the flip run read a stranger's chain): a pid file per data dir killed at start, a node whose log says a port was in use fails the run at once, a port base per case (IGNEUM_V5_BASE, BASE= in harness-remote.sh) Co-Authored-By: Claude Fable 5.1 --- infra/fast-time/class-v5-signal.mjs | 11 ++++++++++- tools/class-v5/harness-remote.sh | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/infra/fast-time/class-v5-signal.mjs b/infra/fast-time/class-v5-signal.mjs index 72e2c583c..fd65d2193 100644 --- a/infra/fast-time/class-v5-signal.mjs +++ b/infra/fast-time/class-v5-signal.mjs @@ -39,7 +39,7 @@ const IGNEUMD = process.env.IGNEUMD || `${BIN}/igneumd`; const CPU_MINER = process.env.IGNEUM_MINER || `${BIN}/igneum-miner`; const IGNEUM_POW = process.env.IGNEUM_POW || `${ROOT}igneum-pow/target/release/igneum-pow`; const TMP = process.env.IGNEUM_V5_TMP || '/tmp/igneum-fast-time-v5s'; -const BASE = 29760, SUFFIX = 976; +const BASE = +(process.env.IGNEUM_V5_BASE || 29760), SUFFIX = 976; const NEVER = '18446744073709551615'; const args = process.argv.slice(2); const flag = (name, dflt) => { const i = args.indexOf(`--${name}`); return i >= 0 ? +args[i + 1] : dflt; }; @@ -63,7 +63,13 @@ const log = (...a) => console.log(new Date().toISOString().slice(11, 23), ...a); const sleep = (ms) => new Promise(r => setTimeout(r, ms)); for (const b of [IGNEUMD, CPU_MINER]) if (!existsSync(b)) { console.error(`missing ${b}`); process.exit(2); } +// the previous run's survivors (the flip-stale run of 7 October 2026, 08:49Z: two nodes of the earlier case were still bound +// to the ports and the harness read a stranger's chain): the pid file of this data dir is killed first, by pid, and a node +// whose log says a port was in use fails the run at once +const PIDS = `${TMP}/pids`; +try { for (const pid of readFileSync(PIDS, 'utf8').split('\n').map(Number).filter(Boolean)) { try { process.kill(pid, 'SIGKILL'); console.log(`killed survivor pid ${pid} of the previous run`); } catch { } } } catch { } rmSync(TMP, { recursive: true, force: true }); mkdirSync(TMP, { recursive: true }); +const recordPid = (p) => { try { writeFileSync(PIDS, `${readFileSync(PIDS, 'utf8')}${p.pid}\n`); } catch { writeFileSync(PIDS, `${p.pid}\n`); } }; const baseText = readFileSync(FILE, 'utf8'); const field = (name) => { const m = new RegExp(`"${name}":\\s*([0-9]+)`).exec(baseText); return m ? +m[1] : undefined; }; const EPOCH = field('pow_epoch_blocks'); @@ -108,7 +114,9 @@ class Node { const byte = this.i < SIGNAL.length ? SIGNAL[this.i] : Math.max(...SIGNAL); this.proc = spawn(IGNEUMD, a, { stdio: ['ignore', out, out], env: { ...process.env, IGNEUM_CLASS_SIGNAL: String(byte) } }); started.push(this.proc); + recordPid(this.proc); await sleep(1200); + if (this.grepLog(/Address already in use|AddrInUse/).length) { log(`FAILED: n${this.i} could not bind its ports (a previous run's node is still alive): ${this.grepLog(/AddrInUse|Address already in use/)[0].slice(0, 160)}`); await stopAll(); process.exit(4); } this.rpc = await connectRpc(`ws://127.0.0.1:${this.jsonPort}`); log(`n${this.i} up pid ${this.proc.pid} json ${this.jsonPort} p2p ${this.p2pPort} exec ${this.stateless ? 'disabled' : this.execPort}, signals ${byte}`); return this; @@ -119,6 +127,7 @@ function miner(bin, argv, name, env = {}) { const out = openSync(`${TMP}/${name}.log`, 'a'); const p = spawn(bin, argv, { stdio: ['ignore', out, out], env: { ...process.env, ...env } }); started.push(p); + recordPid(p); return p; } async function stopAll() { diff --git a/tools/class-v5/harness-remote.sh b/tools/class-v5/harness-remote.sh index 981b08ca3..daa378112 100755 --- a/tools/class-v5/harness-remote.sh +++ b/tools/class-v5/harness-remote.sh @@ -8,6 +8,8 @@ # # tools/class-v5/harness-remote.sh -- # tools/class-v5/harness-remote.sh failed-case -- --signal 5,5,4 --expect flip +# BASE=29830 tools/class-v5/harness-remote.sh flip-stale -- --signal 5,5,5 --expect flip --stale 2 (another port base per +# case, so two cases never share ports; the harness kills the previous run's pids of its own data dir first) set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT="$(cd "$HERE/../.." && pwd)" @@ -21,9 +23,10 @@ REMOTE_LOG="/srv/builds/_log/v5-class/harness" OUT="$ROOT/docs/design/class-v5-harness" mkdir -p "$OUT" ARGS="$*" +BASE="${BASE:-29760}" echo "harness-remote: case $CASE on $HOST: node $REMOTE_ROOT/infra/fast-time/class-v5-signal.mjs $ARGS" # the harness file travels by rsync (the mirror carries HEAD; an uncommitted change rides along, re-stamped: the copied-sources rule) rsync -a -e "ssh -i $KEY" "$ROOT/infra/fast-time/class-v5-signal.mjs" "$ROOT/infra/fast-time/override-60x.json" "$HOST:$REMOTE_ROOT/infra/fast-time/" -ssh -i "$KEY" "$HOST" "touch $REMOTE_ROOT/infra/fast-time/class-v5-signal.mjs $REMOTE_ROOT/infra/fast-time/override-60x.json; mkdir -p $REMOTE_LOG; cd $REMOTE_ROOT && IGNEUM_V5_TMP=/tmp/igneum-fast-time-v5s-$CASE IGNEUM_V5_BIN=$REMOTE_ROOT/vendor/igneum-node-class-v5/target/release IGNEUM_POW=$REMOTE_ROOT/igneum-pow/target/release/igneum-pow node infra/fast-time/class-v5-signal.mjs $ARGS > $REMOTE_LOG/$CASE.log 2>&1; rc=\$?; cp /tmp/igneum-fast-time-v5s-$CASE/summary.json $REMOTE_LOG/$CASE.json 2>/dev/null || true; echo \"harness-remote: rc \$rc\"; exit \$rc" || true +ssh -i "$KEY" "$HOST" "touch $REMOTE_ROOT/infra/fast-time/class-v5-signal.mjs $REMOTE_ROOT/infra/fast-time/override-60x.json; mkdir -p $REMOTE_LOG; cd $REMOTE_ROOT && IGNEUM_V5_BASE=$BASE IGNEUM_V5_TMP=/tmp/igneum-fast-time-v5s-$CASE IGNEUM_V5_BIN=$REMOTE_ROOT/vendor/igneum-node-class-v5/target/release IGNEUM_POW=$REMOTE_ROOT/igneum-pow/target/release/igneum-pow node infra/fast-time/class-v5-signal.mjs $ARGS > $REMOTE_LOG/$CASE.log 2>&1; rc=\$?; cp /tmp/igneum-fast-time-v5s-$CASE/summary.json $REMOTE_LOG/$CASE.json 2>/dev/null || true; echo \"harness-remote: rc \$rc\"; exit \$rc" || true rsync -a -e "ssh -i $KEY" "$HOST:$REMOTE_LOG/$CASE.log" "$HOST:$REMOTE_LOG/$CASE.json" "$OUT/" 2>/dev/null || true grep -E "SUMMARY|FAILED CHECK|PROGRAM ID|CLASS SWITCH" "$OUT/$CASE.log" || tail -20 "$OUT/$CASE.log"