Files
pi-auto-reinject/scripts/b003-repro.mjs
T
grayhook 32e6f72eee Phase 15: add B-003 regression script with unit gate and artifact audit
Run compaction fallback, kept inject, and mid-turn steer tests; document
lost-reinject.jsonl auto→null source signature from the original bug.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-18 23:02:17 +07:00

83 lines
2.7 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Phase 15 regression gate for B-003 (mid-turn / missed reinject).
*
* 1. Runs focused unit tests for source fallback, kept inject entries, mid-turn steer.
* 2. Audits lost-reinject.jsonl (if present) for the pre-fix failure signature.
*
* Full two-compact RPC E2E needs a long session; use manual checklist in TODO.md §15.
*/
import { spawnSync } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { resolve, dirname } from "node:path";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const artifactPath = resolve(repoRoot, "lost-reinject.jsonl");
const unitPatterns = [
"test/compaction-source-fallback.test.ts",
"test/kept-window.test.ts",
"test/reinject-mid-turn.test.ts",
"test/reinject-double-compact.test.ts",
];
function runUnitGate() {
const result = spawnSync("npm", ["test", "--", ...unitPatterns], {
cwd: repoRoot,
stdio: "inherit",
});
if (result.status !== 0) {
process.exit(result.status ?? 1);
}
}
function auditLostReinjectArtifact() {
if (!existsSync(artifactPath)) {
console.log("b003-repro: lost-reinject.jsonl not found — skip artifact audit");
return;
}
const lines = readFileSync(artifactPath, "utf8").split("\n").filter(Boolean);
const stateEntries = [];
const injectEntries = [];
for (const line of lines) {
try {
const entry = JSON.parse(line);
if (entry.customType === "skill-reinject:state" && entry.data) {
stateEntries.push({
id: entry.id,
ts: entry.timestamp,
lastCompactionSource: entry.data.lastCompactionSource,
pendingReinject: entry.data.pendingReinject ?? [],
});
}
if (entry.type === "custom_message" && entry.customType === "skill-reinject:inject") {
injectEntries.push({ id: entry.id, ts: entry.timestamp });
}
} catch {
// skip malformed lines
}
}
const nullSourceAfterAuto = stateEntries.filter(
(entry, index) =>
index > 0 &&
entry.lastCompactionSource === null &&
stateEntries[index - 1]?.lastCompactionSource === "auto",
);
console.log("b003-repro artifact audit:");
console.log(` state snapshots: ${stateEntries.length}`);
console.log(` skill-reinject:inject messages: ${injectEntries.length}`);
console.log(` auto→null source transitions (pre-fix bug): ${nullSourceAfterAuto.length}`);
if (nullSourceAfterAuto.length > 0) {
console.log(" sample null-source entry:", JSON.stringify(nullSourceAfterAuto[0]));
console.log(
" post-fix expectation: ensureCompactionSourceMarked on session_compact → lastCompactionSource: auto",
);
}
}
console.log("b003-repro: running unit regression gate…");
runUnitGate();
auditLostReinjectArtifact();
console.log("b003-repro: PASS (unit gate)");