Files
stack/agents/filbert/work/slice1-s4-review/probe.mjs
T

126 lines
5.9 KiB
JavaScript

// Row 39 S4 round 1 probes (Filbert). Run from the candidate tree:
// PROBE_REPO=<tree> node probe.mjs
import { appendFileSync, chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
const R = process.env.PROBE_REPO;
const N = await import(pathToFileURL(join(R, "packages/cli/src/notifier.mjs")));
const base = mkdtempSync(join(homedir(), "filbert-scratch/r39/probe-"));
let pass = 0;
let fail = 0;
const check = (id, ok, what) => {
if (ok) pass++;
else fail++;
console.log(`${ok ? "PASS" : "FAIL"} ${id} ${what}`);
};
const refuses = (fn) => {
try {
fn();
return null;
} catch (e) {
return e;
}
};
// J1: torn final line, then one confirmed send, then a restart.
{
const f = join(base, "j1", "sent.jsonl");
mkdirSync(join(base, "j1"), { mode: 0o700 });
writeFileSync(f, `${JSON.stringify({ at: "x", kind: "dm", decision: "d1", outcome: "confirmed", messageId: "m1" })}\n{"at":"x","kind":"dm","dec`, { mode: 0o600 });
const logs = [];
const j = N.openJournal(f, { log: (l) => logs.push(l) });
check("J1a", logs.length === 1 && j.sent.has("d1"), "first open skips the torn line and logs it");
j.append({ at: "y", kind: "dm", decision: "d2", outcome: "confirmed", messageId: "m2" });
const e = refuses(() => N.openJournal(f));
console.log(` J1 file after append: ${JSON.stringify(readFileSync(f, "utf8").split("\n")[1].slice(0, 80))}`);
check("J1b", e === null, `restart after one more send opens the journal (got ${e ? `exit ${e.exitCode}: ${e.message}` : "ok"})`);
}
// J2: journal directory with a loose mode.
{
const d = join(base, "j2");
mkdirSync(d, { mode: 0o755 });
chmodSync(d, 0o755);
const e = refuses(() => N.openJournal(join(d, "sent.jsonl")));
console.log(` J2 dir mode ${(statSync(d).mode & 0o777).toString(8)}; open ${e ? `refused: ${e.message}` : "accepted"}`);
}
// J3: sent.jsonl is a symlink to a 0600 file elsewhere.
{
const d = join(base, "j3");
mkdirSync(d, { mode: 0o700 });
const target = join(base, "elsewhere.jsonl");
writeFileSync(target, "", { mode: 0o600 });
symlinkSync(target, join(d, "sent.jsonl"));
const e = refuses(() => N.openJournal(join(d, "sent.jsonl")));
console.log(` J3 symlinked journal: ${e ? `refused: ${e.message}` : "accepted"}`);
}
// J4: a confirmed line with a non-string decision or a missing day loads.
{
const d = join(base, "j4");
mkdirSync(d, { mode: 0o700 });
const f = join(d, "sent.jsonl");
writeFileSync(f, `${JSON.stringify({ kind: "digest", outcome: "confirmed" })}\n${JSON.stringify({ kind: "dm", outcome: "confirmed", decision: 7 })}\n`, { mode: 0o600 });
const e = refuses(() => N.openJournal(f));
console.log(` J4 loose-typed confirmed lines: ${e ? `refused: ${e.message}` : "accepted"}`);
}
// D1: the digest nonce ignores the business.
check("D1", N.digestNonce("2026-10-08") === "dg2026-10-08", `digest nonce is ${N.digestNonce("2026-10-08")} for every business`);
// D2: digest and DM content stay within 1..2000 chars over a sweep.
{
let worst = 0;
let bad = 0;
const mk = (i, qlen, blocking) => ({ id: `${String(i).padStart(8, "0")}-aaaa-bbbb`, action: "release.push", question: "q".repeat(qlen), blocking, options: [{ key: "yes", text: "y".repeat(200) }, { key: "no", text: "n" }], recommendation: "no", raised_by_role: "coder", task_ref: "T-1" });
for (let n = 0; n <= 60; n++) {
for (const qlen of [1, 50, 159, 160, 161, 400, 5000]) {
const inbox = Array.from({ length: n }, (_, i) => mk(i, qlen, i % 2 === 0));
const c = N.digestContent("acme", "2026-10-08", inbox, (id) => id.startsWith("0"));
worst = Math.max(worst, c.length);
if (c.length < 1 || c.length > 2000) bad++;
if (n > 0 && !c.includes("mosaic inbox")) bad++;
for (const d of inbox.slice(0, 2)) {
const m = N.dmContent("acme", d);
worst = Math.max(worst, m.length);
if (m.length < 1 || m.length > 2000) bad++;
}
}
}
check("D2", bad === 0, `digest/DM length sweep (427 inboxes), longest ${worst}`);
}
// D3: DST: 08:00 local is 13:00Z in summer and 14:00Z in winter.
check("D3", N.zoned(new Date("2026-07-01T13:00:00Z")).hour === 8 && N.zoned(new Date("2026-12-01T14:00:00Z")).hour === 8 && N.zoned(new Date("2026-11-01T13:30:00Z")).hour === 7, "zoned 08:00 in CDT and CST; 2026-11-01 13:30Z is 07 CST");
// D4: digest catch-up: a tick at 07:59 local sends nothing; at 08:00 sends one; second tick none.
{
const d = join(base, "d4");
let t = new Date("2026-12-01T13:59:00Z");
const sent = [];
const n = N.createNotifier({ business: "acme", dataRoot: d, inbox: async () => [], direct: { send: async (m) => (sent.push(m), { messageId: `m${sent.length}` }) }, now: () => t });
await n.tick();
const a = sent.length;
t = new Date("2026-12-01T14:00:00Z");
await n.tick();
await n.tick();
check("D4", a === 0 && sent.length === 1 && sent[0].nonce === "dg2026-12-01", `digest edge: before ${a}, after ${sent.length}`);
}
// D5: a send that throws a non-RestOutcome (a bug) is journaled unknown and retried, not fatal.
{
const d = join(base, "d5");
let t = new Date("2026-12-01T10:00:00Z");
let calls = 0;
const n = N.createNotifier({ business: "acme", dataRoot: d, inbox: async () => [{ id: "dec-1", blocking: true, action: "a", question: "q", options: [{ key: "y", text: "y" }], recommendation: "y", raised_by_role: "coder" }], direct: { send: async () => { calls++; throw new TypeError("boom"); } }, now: () => t });
const r = await n.tick();
const lines = readFileSync(join(d, "notify", "acme", "sent.jsonl"), "utf8").trim().split("\n").map((l) => JSON.parse(l));
check("D5", r.failed === 2 && lines.every((l) => l.outcome === "unknown"), `TypeError from send: failed ${r.failed}, outcomes ${lines.map((l) => l.outcome).join(",")}`);
}
console.log(`\n${pass} PASS, ${fail} FAIL`);
rmSync(base, { recursive: true, force: true });