v0.3.1: survival behaviour overhaul — storyline, biome-aware scout, wedge-relocate, food/perf fixes, monitor TUI #28

Merged
halofourteen merged 9 commits from v0.3.1 into main 2026-05-28 09:43:57 +03:00
2 changed files with 31 additions and 2 deletions
Showing only changes of commit b68d4b3ee7 - Show all commits
+9 -1
View File
@@ -39,7 +39,15 @@ export function attach({ intervalMs = TUNE_INTERVAL_MS } = {}) {
return;
}
_timer = setInterval(() => {
runOnce().catch((e) => warn("tuner", `tick err: ${e?.message ?? e}`));
// runOnce is synchronous (pure SQL, no await) — must NOT call
// .catch on its plain-object return. A try/catch here is the
// correct guard. (This exact bug crashed the live bot after
// ~1h uptime when the first tuner tick fired.)
try {
runOnce();
} catch (e) {
warn("tuner", `tick err: ${e?.message ?? e}`);
}
}, intervalMs);
_timer.unref?.();
info("tuner", `attached; tune every ${Math.round(intervalMs / 60000)} min`);
+22 -1
View File
@@ -6,10 +6,31 @@ import { join } from "node:path";
import { initKnowledge, isAvailable, listImprovements } from "../knowledge/index.js";
import { closeStore, __resetForTests } from "../knowledge/store.js";
import { runOnce, __testing } from "./trigger-tuner.js";
import { runOnce, attach, detach, __testing } from "./trigger-tuner.js";
const { MIN_SAMPLE } = __testing;
test("attach: timer tick does not crash (runOnce is sync, regression for .catch bug)", async () => {
// Reproduces the crash that killed the live bot after ~1h: the
// setInterval body called runOnce().catch(...) but runOnce returns
// a plain object, not a Promise. attach must guard with try/catch.
detach();
let threw = false;
const origSetInterval = globalThis.setInterval;
let captured = null;
// capture the interval callback without actually waiting
globalThis.setInterval = (fn) => { captured = fn; return { unref() {} }; };
try {
attach({ intervalMs: 999999 });
// invoke the captured tick synchronously — must not throw
try { captured?.(); } catch { threw = true; }
} finally {
globalThis.setInterval = origSetInterval;
detach();
}
assert.equal(threw, false, "tuner timer tick must not throw");
});
async function bootstrap() {
const tmp = mkdtempSync(join(tmpdir(), "pepa-tuner-test-"));
__resetForTests();