/* RFC-1139 AC-6: mission.open's pre-flight cleanup reports non-empty missions// dirs lacking mission.yaml as stale remnants with a cleanup hint — instead of the generic "non-empty real directory" warn. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-1311: Compass header sweep — declares the mission suite's contract for test-file scaffold coverage. */ import { describe, expect, it, beforeEach, afterEach } from "vitest"; import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { cleanupStaleMissionEntries } from "../mission-open.ts"; describe("mission.open — RFC-1139 stale remnant detection", () => { let root: string; const warns: string[] = []; const logger = { info: () => {}, warn: (msg: string) => warns.push(msg), }; beforeEach(async () => { root = await mkdtemp(join(tmpdir(), "mission-remnant-")); warns.length = 0; }); afterEach(async () => { await rm(root, { recursive: true, force: true }); }); it("AC-6: non-empty dir without mission.yaml is reported as a stale remnant", async () => { const remnant = join(root, "missions", "acme-m000148", "workpiece-failed"); await mkdir(remnant, { recursive: true }); await writeFile(join(remnant, "failure-report.json"), "{}\n", "utf8"); const result = await cleanupStaleMissionEntries(root, logger); expect(result.skipped).toContain("missions/acme-m000148"); expect(result.removedPaths).not.toContain("missions/acme-m000148"); const remnantWarn = warns.find((w) => w.includes("acme-m000148")); expect(remnantWarn).toBeDefined(); expect(remnantWarn).toContain("Stale mission remnant"); expect(remnantWarn).toContain("mission.archive --clean-orphans"); }); it("non-empty dir WITH mission.yaml keeps the generic skip warning", async () => { const live = join(root, "missions", "acme-m000149"); await mkdir(live, { recursive: true }); await writeFile(join(live, "mission.yaml"), "missionId: acme-m000149\n", "utf8"); const result = await cleanupStaleMissionEntries(root, logger); expect(result.skipped).toContain("missions/acme-m000149"); const warn = warns.find((w) => w.includes("acme-m000149")); expect(warn).toBeDefined(); expect(warn).toContain("Skipping non-empty real directory"); expect(warn).not.toContain("Stale mission remnant"); }); it("RFC-1157: cache-only husk (no mission.yaml, only workpiece cache dirs) is trashed", async () => { const husk = join(root, "missions", "acme-m000151", "workpiece", ".astro"); await mkdir(husk, { recursive: true }); await writeFile(join(husk, "data-store.json"), "{}\n", "utf8"); await mkdir(join(root, "missions", "acme-m000151", "workpiece", "node_modules"), { recursive: true, }); const result = await cleanupStaleMissionEntries(root, logger); expect(result.removedPaths).toContain("missions/acme-m000151"); expect(result.skipped).not.toContain("missions/acme-m000151"); expect(warns.some((w) => w.includes("Stale mission remnant"))).toBe(false); }); it("RFC-1157: dir without mission.yaml but with real content still warns and skips", async () => { const remnant = join(root, "missions", "acme-m000152", "workpiece"); await mkdir(remnant, { recursive: true }); await writeFile(join(remnant, "package.json"), "{}\n", "utf8"); const result = await cleanupStaleMissionEntries(root, logger); expect(result.skipped).toContain("missions/acme-m000152"); expect(result.removedPaths).not.toContain("missions/acme-m000152"); const warn = warns.find((w) => w.includes("acme-m000152")); expect(warn).toContain("Stale mission remnant"); }); it("empty dirs are still cleaned without remnant reporting", async () => { await mkdir(join(root, "missions", "acme-m000150"), { recursive: true }); const result = await cleanupStaleMissionEntries(root, logger); expect(result.removedPaths).toContain("missions/acme-m000150"); expect(warns.some((w) => w.includes("Stale mission remnant"))).toBe(false); }); });