Filbert approved round 1 (f167b85e). Manifest 782bcb62, 21 files, plus the QUEUE.md markers and the TOOLS.md section. Lead decision 35. Co-Authored-By: Claude Opus 5.5 <[email protected]>
153 lines
8.5 KiB
JavaScript
153 lines
8.5 KiB
JavaScript
// Scratch repositories for the queue tests. Every test works under
|
|
// os.tmpdir(): a fresh repository holding a copy of packages/queue/src and
|
|
// the two discord helpers it imports, so the code-under-root check (8.3)
|
|
// passes there and nothing touches this checkout's .git.
|
|
import { spawnSync } from "node:child_process";
|
|
import { cpSync, mkdirSync, mkdtempSync, realpathSync, rmSync, statfsSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
export const PKG_SRC = join(HERE, "..", "src");
|
|
export const DISCORD_SRC = join(HERE, "..", "..", "discord", "src");
|
|
export const SCRIPTS = join(HERE, "..", "..", "..", "scripts");
|
|
|
|
export const BEGIN = "<!-- mosaic-queue:begin -->";
|
|
export const END = "<!-- mosaic-queue:end -->";
|
|
|
|
// Git reads no user or system config here: HOME is a scratch directory.
|
|
export function gitEnv(home, extra = {}) {
|
|
const env = { ...process.env, HOME: home, XDG_CONFIG_HOME: join(home, ".config"), GIT_CONFIG_NOSYSTEM: "1",
|
|
GIT_AUTHOR_NAME: "t", GIT_AUTHOR_EMAIL: "[email protected]", GIT_COMMITTER_NAME: "t", GIT_COMMITTER_EMAIL: "[email protected]", ...extra };
|
|
for (const k of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE", "MOSAIC_AGENT_NAME"]) if (!(k in extra)) delete env[k];
|
|
return env;
|
|
}
|
|
|
|
export function sh(cmd, args, { cwd, env, input, allowFail = false } = {}) {
|
|
const r = spawnSync(cmd, args, { cwd, env, input, encoding: "utf8", maxBuffer: 64 << 20 });
|
|
if (r.error) throw r.error;
|
|
if (r.status !== 0 && !allowFail) throw new Error(`${cmd} ${args.join(" ")} exited ${r.status}: ${r.stderr}`);
|
|
return r;
|
|
}
|
|
|
|
export const MAP_ROWS = [
|
|
{
|
|
id: 1, piece: "Finished thing", owner: "darkwing", issues: [1503], closes: [1503], state: "done", previousState: null,
|
|
required: false, requiredSince: null, gate: "B passed", gateOwner: "jason", brief: null, after: [], reviewers: [],
|
|
note: null, blockedReason: null, createdAt: "unknown",
|
|
},
|
|
{
|
|
id: 6, piece: "Row six", owner: "darkwing", issues: [1511], closes: [1511], state: "in-progress", previousState: null,
|
|
required: false, requiredSince: null, gate: "pilot accepted", gateOwner: "jason", brief: { path: "docs/plans/brief-a.md", anchor: "Row six" },
|
|
after: [], reviewers: ["filbert"], note: "legacy note", blockedReason: null, createdAt: "2026-09-13",
|
|
},
|
|
{
|
|
id: 8, piece: "Parked thing", owner: "unassigned", issues: [], closes: [], state: "parked", previousState: null,
|
|
required: false, requiredSince: null, gate: "Jason says", gateOwner: "jason", brief: { path: "docs/plans/brief-a.md", anchor: "Parked" },
|
|
after: [], reviewers: [], note: null, blockedReason: null, createdAt: "unknown",
|
|
},
|
|
{
|
|
id: 9, piece: "Queue as data", owner: "darkwing", issues: [1508], closes: [1508], state: "briefed", previousState: null,
|
|
required: true, requiredSince: "unknown", gate: "filbert approves", gateOwner: "filbert", brief: { path: "docs/plans/brief-b.md", anchor: "Queue" },
|
|
after: [{ id: 6, when: "settled" }], reviewers: ["filbert"], note: null, blockedReason: null, createdAt: "2026-09-13",
|
|
},
|
|
{
|
|
id: 11, piece: "Brief template", owner: "dewey", issues: [1508], closes: [1508], state: "briefed", previousState: null,
|
|
required: false, requiredSince: null, gate: "two briefs accepted", gateOwner: "jason", brief: { path: "docs/plans/brief-b.md", anchor: "Template" },
|
|
after: [], reviewers: [], note: null, blockedReason: null, createdAt: "unknown",
|
|
},
|
|
];
|
|
|
|
export function mapText(rows = MAP_ROWS, { retired = [7], highWater = 11 } = {}) {
|
|
return `# Migration map\n\nReviewed.\n\n\`\`\`json queue-map\n${JSON.stringify({ rows, retired, highWater }, null, 2)}\n\`\`\`\n`;
|
|
}
|
|
|
|
export const LEGACY = "| # | Piece |\n|---|---|\n| 1 | old row |\n";
|
|
|
|
export function queueMd(body = LEGACY) {
|
|
return `# QUEUE\n\nHeader prose.\n\n${BEGIN}\n${body}${END}\n\nParked entries below.\n`;
|
|
}
|
|
|
|
// A committed scratch checkout on branch `refactor` with the queue code,
|
|
// seats, briefs, a map and QUEUE.md. Returns paths and runners. With
|
|
// `scripts`, it also commits queue-commit.sh, the guard and a one-test
|
|
// packages/queue/tests, so the commit procedure has an archive to run.
|
|
export function scratchRepo(t, { map = mapText(), commitMap = true, name = "repo", scripts = false } = {}) {
|
|
// The CLI refuses tmpfs (N5), and the child-process tests can't pass it a
|
|
// test layer, so a tmpfs temp directory would fail them all confusingly.
|
|
if (statfsSync(tmpdir()).type === 0x01021994) {
|
|
throw new Error(`${tmpdir()} is tmpfs, which the queue refuses; set TMPDIR to a directory on ext4, xfs or btrfs`);
|
|
}
|
|
const base = realpathSync(mkdtempSync(join(tmpdir(), "mosaic-queue-test-")));
|
|
if (t) t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
const home = join(base, "home");
|
|
mkdirSync(join(home, ".config"), { recursive: true });
|
|
const root = join(base, name);
|
|
mkdirSync(root);
|
|
const env = gitEnv(home);
|
|
const g = (...args) => sh("git", ["-C", root, ...args], { env }).stdout;
|
|
g("init", "-q", "-b", "refactor");
|
|
cpSync(PKG_SRC, join(root, "packages/queue/src"), { recursive: true });
|
|
mkdirSync(join(root, "packages/discord/src"), { recursive: true });
|
|
for (const f of ["journal.mjs", "errors.mjs"]) cpSync(join(DISCORD_SRC, f), join(root, "packages/discord/src", f));
|
|
for (const s of ["darkwing", "dewey", "filbert", "rocko", "sage"]) {
|
|
mkdirSync(join(root, "agents", s, "work"), { recursive: true });
|
|
writeFileSync(join(root, "agents", s, ".keep"), "");
|
|
}
|
|
mkdirSync(join(root, "docs/plans"), { recursive: true });
|
|
writeFileSync(join(root, "docs/plans/brief-a.md"), "# Briefs A\n\n## Row six\n\nText.\n\n## Parked\n\nStub.\n");
|
|
writeFileSync(join(root, "docs/plans/brief-b.md"), "# Briefs B\n\n## Queue\n\nText.\n\n## Template\n\nText.\n\n```\n## Queue\n```\n");
|
|
writeFileSync(join(root, "docs/plans/QUEUE.md"), queueMd());
|
|
if (map !== null) writeFileSync(join(root, "agents/sage/work/queue-migration-map.md"), map);
|
|
if (scripts) {
|
|
mkdirSync(join(root, "scripts/git-hooks"), { recursive: true });
|
|
cpSync(join(SCRIPTS, "queue-commit.sh"), join(root, "scripts/queue-commit.sh"));
|
|
cpSync(join(SCRIPTS, "git-hooks/pre-commit"), join(root, "scripts/git-hooks/pre-commit"));
|
|
mkdirSync(join(root, "packages/queue/tests"), { recursive: true });
|
|
writeFileSync(join(root, "packages/queue/tests/stub.test.mjs"), 'import { test } from "node:test";\ntest("archive stub", () => {});\n');
|
|
}
|
|
if (!commitMap && map !== null) {
|
|
g("add", "-A", "--", ".", ":!agents/sage/work/queue-migration-map.md");
|
|
} else {
|
|
g("add", "-A");
|
|
}
|
|
g("commit", "-q", "-m", "scratch");
|
|
return { base, home, root, env, g, gitDir: join(root, ".git"), queuePath: join(root, "docs/plans/queue.json"), viewPath: join(root, "docs/plans/QUEUE.md") };
|
|
}
|
|
|
|
// The copied code, imported from inside the scratch repository.
|
|
export async function load(repo) {
|
|
const src = join(repo.root, "packages/queue/src");
|
|
const store = await import(pathToFileURL(join(src, "store.mjs")).href);
|
|
const cli = await import(pathToFileURL(join(src, "cli.mjs")).href);
|
|
const queue = await import(pathToFileURL(join(src, "queue.mjs")).href);
|
|
const io = await import(pathToFileURL(join(src, "io.mjs")).href);
|
|
const lock = await import(pathToFileURL(join(src, "lock.mjs")).href);
|
|
const errors = await import(pathToFileURL(join(src, "errors.mjs")).href);
|
|
return { store, cli, queue, io, lock, errors };
|
|
}
|
|
|
|
// Runs the copied CLI as a child process in `cwd`.
|
|
export function cli(repo, args, { cwd = repo.root, by = null, env = {} } = {}) {
|
|
const e = { ...repo.env, ...env };
|
|
if (by !== null) e.MOSAIC_AGENT_NAME = by;
|
|
const r = spawnSync(process.execPath, [join(repo.root, "packages/queue/src/cli.mjs"), ...args], { cwd, env: e, encoding: "utf8" });
|
|
if (r.error) throw r.error;
|
|
return { code: r.status, signal: r.signal, out: r.stdout, err: r.stderr };
|
|
}
|
|
|
|
// Genesis in the working tree, then the genesis commit by plain git (the
|
|
// scratch repo has no guard installed), so later ops pass the F3 check.
|
|
export function genesisCommitted(repo, { by = "sage", op = "genesis-2026-09-26" } = {}) {
|
|
const r = cli(repo, ["genesis", "--op", op, "--root", repo.root, "--branch", "refactor", "--map", "agents/sage/work/queue-migration-map.md"], { by });
|
|
if (r.code !== 0) throw new Error(`genesis failed: ${r.err}`);
|
|
repo.g("add", "docs/plans/queue.json", "docs/plans/QUEUE.md");
|
|
repo.g("commit", "-q", "-m", "queue genesis");
|
|
return r;
|
|
}
|
|
|
|
export function opts(repo, extra = {}) {
|
|
return { cwd: repo.root, env: repo.env, ...extra };
|
|
}
|