packages/queue, scripts/queue-commit.sh, scripts/git-hooks and scripts/test-queue.sh, plus docs/plans/BRIEF-TEMPLATE.md. There is no queue.json yet, so verify skips until the genesis commit after A2. Darkwing built it, and Filbert reviewed R0 (6933b885, changes requested) and r1 (e464be6c, approved). The 20 files match manifest 85a8a453. The nine suites passed on an index export, including the new queue suite. test-queue.sh joins the suite list in AGENTS.md. Lead decisions 20, 23 and 26. Co-Authored-By: Claude Opus 5.5 <[email protected]>
148 lines
8.2 KiB
JavaScript
148 lines
8.2 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, 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 } = {}) {
|
|
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 };
|
|
}
|