/*
RFC-1327: mission.abort run-twice test — aborting an already-aborted mission
is a no-op exit-0 (alreadyAborted), not a "not open" crash; the manifest and
clone state are unchanged by the second run.
- Not an abort-semantics test — dirty-workpiece/unreconciled guards live in the existing abort suites.
- RFC-1327: initial rerun coverage — abort twice, converge.
- RFC-1327: run-twice idempotency — declarations, command.idempotency.validate, rerun tests, reconcile/ship/abort convergence fixes
- RFC-1327: explicit test timeouts on rerun tests — root vitest testTimeout is 5s
*/
import { test, expect, beforeEach, afterEach } from "vitest";
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { execSync } from "node:child_process";
import { tmpdir } from "node:os";
import type { KernelCommandInput, KernelRuntimeContext } from "@warpgogol/werkstatt-engine/kernel";
import { createDefaultIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io";
import { expectRerunSafe, snapshotGitTree } from "../testing/run-twice.ts";
function gitInit(dir: string): void {
execSync("git init -b main", { cwd: dir, stdio: "pipe" });
execSync("git config user.email test@test.com", { cwd: dir, stdio: "pipe" });
execSync("git config user.name Test", { cwd: dir, stdio: "pipe" });
}
function gitCommit(dir: string, msg: string): void {
execSync("git add -A", { cwd: dir, stdio: "pipe" });
execSync(`git commit -qm ${JSON.stringify(msg)}`, { cwd: dir, stdio: "pipe" });
}
let testRoot: string;
let tmpWorkspace: string;
let cacheDir: string;
beforeEach(() => {
testRoot = mkdtempSync(join(tmpdir(), "tmp-rerun-abort-"));
tmpWorkspace = join(testRoot, "werkstatt");
mkdirSync(tmpWorkspace, { recursive: true });
});
afterEach(() => {
rmSync(testRoot, { recursive: true, force: true });
});
function setup(): void {
gitInit(tmpWorkspace);
writeFileSync(join(tmpWorkspace, "README.md"), "# test\n");
gitCommit(tmpWorkspace, "initial");
cacheDir = join(testRoot, "systems-cache", "test-system");
mkdirSync(cacheDir, { recursive: true });
writeFileSync(
join(cacheDir, "system-state.yaml"),
`schemaVersion: system-state/v1
systemId: test-system
currentMission: test-system-m000001
lastRelease: null
`,
);
gitInit(cacheDir);
writeFileSync(join(cacheDir, "README.md"), "# system\n");
gitCommit(cacheDir, "initial system");
const missionDir = join(tmpWorkspace, "missions", "test-system-m000001");
mkdirSync(join(missionDir, "evidence"), { recursive: true });
writeFileSync(
join(missionDir, "mission.yaml"),
JSON.stringify(
{
schemaVersion: "1.0.0",
missionId: "test-system-m000001",
systemId: "test-system",
state: "open",
brief: "Test mission",
openedAt: "2026-10-17T00:00:00.000Z",
openedBy: "test-agent",
closedAt: null,
closedBy: null,
pinAtOpen: "1.0.0",
materializedAt: "2026-10-17T01:00:00.000Z",
migratedAt: "2026-10-17T01:00:00.000Z",
reconciledAt: "2026-10-17T02:00:00.000Z",
releaseId: null,
rfcId: null,
operationId: "op-001",
},
null,
2,
) + "\n",
);
const workpieceDir = join(missionDir, "workpiece");
execSync(`git clone ${JSON.stringify(cacheDir)} ${JSON.stringify(workpieceDir)}`, {
stdio: "pipe",
});
gitCommit(tmpWorkspace, "add mission");
}
test("mission.abort twice: second run is a no-op, not a crash (RFC-1327)", { timeout: 60_000 }, async () => {
setup();
const { runMissionAbort } = await import("./mission-abort.ts");
const input = {
flags: { mission: "test-system-m000001", reason: "superseded" },
} as unknown as KernelCommandInput;
const context = {
io: createDefaultIO().io,
workspaceRoot: tmpWorkspace,
logger: { info: () => {}, warn: () => {} },
} as unknown as KernelRuntimeContext;
const { first, second } = await expectRerunSafe({
run: async () => {
const r = await runMissionAbort(input, context);
return { exitCode: r.exitCode, status: r.exitCode === 0 ? "ok" : "failed" };
},
snapshot: () => `${snapshotGitTree(tmpWorkspace)}\n${snapshotGitTree(cacheDir)}`,
});
expect(first.exitCode).toBe(0);
expect(second.exitCode).toBe(0);
});