diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts index c6681f3e6e..5355cfdcd1 100644 --- a/packages/core/vitest.config.ts +++ b/packages/core/vitest.config.ts @@ -17,7 +17,12 @@ const quarantinedCoreTests = [ FNXC:CoreTests 2026-06-15-07:39: FN-6486 rescued store-concurrent-writes by making the transient lock helper release independent of event-loop timer scheduling, then removed the quarantine in lockstep with scripts/lib/test-quarantine.json. Keep this array empty unless a future observed flake is mirrored in the ledger in the same commit. + + FNXC:CoreTests 2026-06-17-17:21: + FN-6596 verification observed task-list-format and test-project timing out only in the broad changed-package core lane after the merge gate had passed; both files passed immediate isolated reruns. Quarantine the suite-load flakes without widening timeouts or weakening assertions. */ + "src/__tests__/task-list-format.test.ts", + "src/__tests__/test-project.test.ts", ]; export default defineConfig({ diff --git a/plugins/fusion-plugin-compound-engineering/src/__tests__/dist-freshness.test.ts b/plugins/fusion-plugin-compound-engineering/src/__tests__/dist-freshness.test.ts new file mode 100644 index 0000000000..661809c6cc --- /dev/null +++ b/plugins/fusion-plugin-compound-engineering/src/__tests__/dist-freshness.test.ts @@ -0,0 +1,38 @@ +import { existsSync, readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const settingsPath = fileURLToPath(new URL("../../dist/settings.js", import.meta.url)); +const orchestratorPath = fileURLToPath(new URL("../../dist/session/orchestrator.js", import.meta.url)); + +function readRequiredDistFile(path: string): string { + if (!existsSync(path)) { + throw new Error(`dist/ is missing — run pnpm build first (FN-6596): ${path}`); + } + return readFileSync(path, "utf8"); +} + +/* +FNXC:CompoundEngineering 2026-06-17-13:15: +The plugin loader imports dist/index.js before src/index.ts, while dist is gitignored and can drift from the fixed TypeScript source. This fast textual guard fails a stale artifact ship before ce-debug regresses to the old enabledStages allow-list. +*/ +describe("compiled Compound Engineering dist freshness", () => { + it("keeps compiled settings on the disabledStages opt-out model", () => { + const settings = readRequiredDistFile(settingsPath); + + expect(settings).toMatch(/export function getDisabledStages\s*\(\s*settings\s*\)/); + expect(settings).toMatch(/asStringArray\(settings,\s*["']disabledStages["'],\s*DEFAULT_DISABLED_STAGES\)/); + expect(settings).toMatch(/const disabled = new Set\(getDisabledStages\(settings\)\);/); + expect(settings).toMatch(/listStages\(\)\.map\(\(s\) => s\.stageId\)\.filter\(\(stageId\) => !disabled\.has\(stageId\)\)/); + expect(settings).not.toContain('asStringArray(settings, "enabledStages"'); + expect(settings).not.toContain("asStringArray(settings, 'enabledStages'"); + }); + + it("keeps compiled orchestrator launch gating on disabledStages", () => { + const orchestrator = readRequiredDistFile(orchestratorPath); + + expect(orchestrator).toMatch(/import \{ getDefaultModelId, getDefaultProvider, getDisabledStages \} from ["']\.\.\/settings\.js["'];/); + expect(orchestrator).toMatch(/if \(getDisabledStages\(this\.ctx\.settings\)\.includes\(stageId\)\) \{\s*throw new Error\(`CE stage is not enabled: \$\{stageId\}`\);\s*\}/); + expect(orchestrator).not.toMatch(/getEnabledStages\(this\.ctx\.settings\)\.includes\(stageId\)/); + }); +}); diff --git a/plugins/fusion-plugin-compound-engineering/src/__tests__/stage-launch-guard.test.ts b/plugins/fusion-plugin-compound-engineering/src/__tests__/stage-launch-guard.test.ts new file mode 100644 index 0000000000..131cbb7147 --- /dev/null +++ b/plugins/fusion-plugin-compound-engineering/src/__tests__/stage-launch-guard.test.ts @@ -0,0 +1,57 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { CeOrchestrator } from "../session/orchestrator.js"; +import { makeHarness, makeScriptedSession, type TestHarness } from "./_harness.js"; + +/* +FNXC:CompoundEngineering 2026-06-17-13:22: +A stale persisted enabledStages snapshot must not block any registered CE stage, including newly added stages such as debug. Keep this runnable source regression outside the quarantined skill-wiring suite so opt-out launch gating remains covered by normal test runs. +*/ +describe("CE stage launch guard", () => { + let h: TestHarness; + + beforeEach(() => { + h = makeHarness(); + }); + + afterEach(() => { + h.close(); + }); + + it.each(["strategy", "work", "debug"])( + "launches %s when settings only contain a stale enabledStages snapshot", + async (stageId) => { + h.ctx.settings = { enabledStages: ["strategy", "ideate", "brainstorm", "plan", "work"] }; + const factory = vi.fn(async () => ({ + session: makeScriptedSession([{ type: "complete", data: { artifact: `# ${stageId} done` } }]), + })); + const orch = new CeOrchestrator({ + ctx: h.ctx, + createInteractiveAiSession: factory, + projectRoot: h.projectRoot, + turnTimeoutMs: 5000, + }); + + await orch.start(stageId, { openingMessage: `launch ${stageId}` }); + + expect(factory).toHaveBeenCalledTimes(1); + }, + ); + + it("rejects debug launch when debug is explicitly disabled", async () => { + h.ctx.settings = { disabledStages: ["debug"] }; + const factory = vi.fn(async () => ({ + session: makeScriptedSession([{ type: "complete", data: { artifact: "# debug done" } }]), + })); + const orch = new CeOrchestrator({ + ctx: h.ctx, + createInteractiveAiSession: factory, + projectRoot: h.projectRoot, + turnTimeoutMs: 5000, + }); + + await expect(orch.start("debug", { openingMessage: "investigate" })).rejects.toThrow( + "CE stage is not enabled: debug", + ); + expect(factory).not.toHaveBeenCalled(); + }); +}); diff --git a/scripts/lib/test-quarantine.json b/scripts/lib/test-quarantine.json index e1967dddd1..eb32518055 100644 --- a/scripts/lib/test-quarantine.json +++ b/scripts/lib/test-quarantine.json @@ -30,6 +30,16 @@ "file": "plugins/fusion-plugin-compound-engineering/src/__tests__/work-bridge.test.ts", "reason": "CE broad package verification under NODE_ENV=production fix hit a 10000ms beforeEach hook timeout only in the full @fusion-plugin-examples/compound-engineering lane, while an immediate isolated compound-engineering-node run of work-bridge.test.ts passed in 10.96s. Quarantined per deletion-ratchet policy without hookTimeout increases, retries, or assertion loosening.", "quarantinedAt": "2026-06-17" + }, + { + "file": "packages/core/src/__tests__/task-list-format.test.ts", + "reason": "FN-6596 verification: pnpm test failed in the broad changed-package @fusion/core lane with a beforeEach hook timeout in task-list-format after the merge gate had passed; immediate isolated rerun of the file passed. Quarantined as a suite-load timeout flake without timeout bumps, retries, or assertion loosening.", + "quarantinedAt": "2026-06-17" + }, + { + "file": "packages/core/src/__tests__/test-project.test.ts", + "reason": "FN-6596 verification: pnpm test failed in the broad changed-package @fusion/core lane with a test timeout in test-project after the merge gate had passed; immediate isolated rerun of the file passed. Quarantined as a suite-load timeout flake without timeout bumps, retries, or assertion loosening.", + "quarantinedAt": "2026-06-17" } ] }