VoidBot/scripts/smoke-repo-face-memory-projection.mjs
2026-05-22 19:46:01 +01:00

111 lines
3.6 KiB
JavaScript

#!/usr/bin/env node
import "dotenv/config";
import { spawn } from "node:child_process";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = resolve(fileURLToPath(new URL("..", import.meta.url)));
const identity = process.argv[2] ?? "libby";
const tempDir = await mkdtemp(join(tmpdir(), "voidbot-face-projection-"));
const outPath = join(tempDir, `${identity}.md`);
try {
const run = await runCommand(process.execPath, [
"node_modules/tsx/dist/cli.mjs",
"scripts/run-repo-face-heartbeats.ts",
"--assemble-prompt",
identity,
"--out",
outPath,
]);
if (run.code !== 0) {
throw new Error(`prompt assembly failed with ${run.code}:\n${run.stderr || run.stdout}`);
}
const prompt = await readFile(outPath, "utf8");
const memorySurface = extractSection(
prompt,
"What you remember, feel, and want right now:",
"Write naturally as",
);
const forbidden = [
/\bgrants:/i,
/\bjurisdictions:/i,
/\bFace of\s+[A-Z][A-Za-z0-9_-]+\b/,
/\brepo=[^\s]+/i,
/\bpath=[^\s]+/i,
/\bdo not prompt\b/i,
/\bprompt (?:her|him|them|it)\b/i,
/^Private truths tugging/im,
/^Values that should bend/im,
/^Live needs and frictions/im,
/^Relationship pressure:/im,
/^Status reads:/im,
/^Agency pressure:/im,
/^You understand yourself this way/im,
];
const leaked = forbidden.find((pattern) => pattern.test(memorySurface));
if (leaked) {
throw new Error(`projected memory surface leaked forbidden pattern ${leaked}:\n${memorySurface}`);
}
if (memorySurface.trim().length < 240) {
throw new Error(`projected memory surface is too thin:\n${memorySurface}`);
}
if (identity.toLowerCase() === "nibu") {
if (
!/\bMetacrat\b[\s\S]{0,320}\bshe\/her\b/i.test(memorySurface) &&
!/\bMetacrat\b[\s\S]{0,320}\bher approval\b/i.test(memorySurface)
) {
throw new Error(`Nibu projection did not preserve Metacrat's explicit she/her pronoun guidance:\n${memorySurface}`);
}
const badMetacratMasculine = [
/\bMetacrat\b[\s\S]{0,240}\bhis attention\b/i,
/\bMetacrat\b[\s\S]{0,240}\bwhen he turns away\b/i,
/\bMetacrat\b[\s\S]{0,240}\bhe listens\b/i,
];
const badPattern = badMetacratMasculine.find((pattern) => pattern.test(memorySurface));
if (badPattern) {
throw new Error(`Nibu projection used masculine pronouns for Metacrat (${badPattern}):\n${memorySurface}`);
}
}
process.stdout.write(`${JSON.stringify({ ok: true, identity, outPath, chars: memorySurface.length })}\n`);
} finally {
await rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
}
function extractSection(text, startMarker, endMarker) {
const start = text.indexOf(startMarker);
const end = text.indexOf(endMarker);
if (start < 0 || end < 0 || end <= start) {
throw new Error(`could not find memory projection section in assembled prompt`);
}
return text.slice(start + startMarker.length, end).trim();
}
function runCommand(command, args) {
return new Promise((resolveRun) => {
const child = spawn(command, args, {
cwd: repoRoot,
env: process.env,
stdio: ["ignore", "pipe", "pipe"],
windowsHide: true,
});
let stdout = "";
let stderr = "";
child.stdout.setEncoding("utf8");
child.stderr.setEncoding("utf8");
child.stdout.on("data", (chunk) => {
stdout += chunk;
});
child.stderr.on("data", (chunk) => {
stderr += chunk;
});
child.on("close", (code, signal) => {
resolveRun({ code, signal, stdout, stderr });
});
});
}