FN-6596: guard compound engineering debug launches

Guard Compound Engineering debug stage launches against stale stage settings and artifacts.

- Add source-level coverage proving stale enabledStages snapshots do not block registered CE stages, including debug.
- Add dist freshness checks for the disabledStages opt-out launch model so stale compiled plugin artifacts fail fast.
- Mirror newly observed core suite-load flakes in the quarantine ledger and Vitest excludes.

Files changed:
 packages/core/vitest.config.ts                     |  5 ++
 .../src/__tests__/dist-freshness.test.ts           | 38 +++++++++++++++
 .../src/__tests__/stage-launch-guard.test.ts       | 57 ++++++++++++++++++++++
 scripts/lib/test-quarantine.json                   | 10 ++++
 4 files changed, 110 insertions(+)

Fusion-Task-Id: FN-6596

Fusion-Task-Lineage: 3cd75e5f-c6f8-4bde-8202-54eeac82b894
This commit is contained in:
gsxdsm
2026-06-17 17:21:22 -07:00
parent 822d5d1872
commit cc793987b2
4 changed files with 110 additions and 0 deletions

View File

@@ -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({

View File

@@ -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\)/);
});
});

View File

@@ -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();
});
});

View File

@@ -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"
}
]
}