Files
pepa-pi-bot/runtime/world-journal.test.js
mayatnikovandClaude Opus 4.7 6560c0765c fix(auto-improve): detach auto-patch + recovery-tunnel-out test in suite
Two bugs the live self-improvement run exposed:

1) Auto-patch was spawned with detached:false, so when supervisor
   restarted bot.js (file change after Pi's commit landed on the
   auto branch), the auto-patch child was killed mid-way — Pi's
   commit lived in the auto branch but never got cherry-picked.
   Recovered manually this round via reflog + cherry-pick. Now
   detached:true + child.unref() + a per-run log at
   state/_auto-patch-last.log so the operator can read Pi's full
   output later.

2) Pi's recovery-tunnel-out.test.js was created but not in npm test
   script; tests would have stayed unrun forever. Added.

Also commits the Pi-authored skill (eb29591 cherry-picked):
- runtime/skills/recovery-tunnel-out.js (+ test)
- improvements to runtime/actions.js + runtime/skills/explore-far.js
- wired into runtime/skills/index.js

npm test 137/137.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 12:43:30 +03:00

68 lines
2.7 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { createWorldJournal, _internal } from "./world-journal.js";
function tag() { return `__t_${Date.now()}_${Math.floor(Math.random() * 1e6)}`; }
// Tests share the real state/<host>/world-journal.jsonl with the live bot,
// so we start each test with a fresh journal to avoid mixing with prior
// runs. This DOES wipe the file on disk — fine for the dev box.
function freshJournal() {
const seed = createWorldJournal();
seed.clear();
return createWorldJournal(); // re-instantiate to drop in-memory state too
}
test("cellOf buckets by GRID_CELL", () => {
assert.equal(_internal.cellOf(0, 0), "0,0");
assert.equal(_internal.cellOf(15, 15), "0,0");
assert.equal(_internal.cellOf(16, 16), "1,1");
assert.equal(_internal.cellOf(-1, -1), "-1,-1");
});
test("append + nearest returns the entry we just stored", () => {
const j = freshJournal();
const name = tag();
j.append({ kind: "chopped", name, at: { x: 100, y: 64, z: 200 } });
const got = j.nearest({ kind: "chopped", x: 100, z: 200, radius: 16, limit: 5 });
const ours = got.find((e) => e.name === name);
assert.ok(ours, "expected to find our own entry back");
assert.equal(ours.at.x, 100);
});
test("nearest ranks by distance and respects radius", () => {
const j = freshJournal();
const t = tag();
j.append({ kind: "stone", name: t + "_far", at: { x: 100, y: 64, z: 100 } });
j.append({ kind: "stone", name: t + "_near", at: { x: 5, y: 64, z: 5 } });
const got = j.nearest({ kind: "stone", x: 0, z: 0, radius: 50, limit: 5 });
const near = got.find((e) => e.name === t + "_near");
const far = got.find((e) => e.name === t + "_far");
assert.ok(near);
assert.equal(far, undefined, "far entry > radius should be excluded");
});
test("leanestQuadrant returns the quadrant with fewest entries", () => {
const j = freshJournal();
const t = tag();
for (let i = 0; i < 5; i++) {
j.append({ kind: "dead_end", name: t, at: { x: 10 + i, y: 64, z: -10 - i } }); // NE
}
for (let i = 0; i < 2; i++) {
j.append({ kind: "dead_end", name: t, at: { x: 10 + i, y: 64, z: 10 + i } }); // SE
}
const { best, counts } = j.leanestQuadrant({ x: 0, z: 0, radius: 64 });
assert.ok(["SW", "NW"].includes(best), `expected unused quadrant, got ${best} counts=${JSON.stringify(counts)}`);
});
test("summary lists per-kind totals", () => {
const j = freshJournal();
j.append({ kind: "chopped", name: "oak_log", at: { x: 1, y: 1, z: 1 } });
j.append({ kind: "chopped", name: "oak_log", at: { x: 2, y: 1, z: 2 } });
j.append({ kind: "shelter", name: "shelter", at: { x: 0, y: 1, z: 0 } });
const s = j.summary();
assert.ok(s.byKind.chopped >= 2);
assert.ok(s.byKind.shelter >= 1);
});