diff --git a/docs/testing.md b/docs/testing.md index 5c6707f0ce..8fa7babb6f 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -150,6 +150,8 @@ Flaky tests are quarantined ON SIGHT and deleted on a 2-week clock. This is writ **CLI shared-fixture rescue pattern (FN-6430):** the 2026-06-14 `@runfusion/fusion` quarantine batch passed direct runs but timed out or bled state only under package/workspace load. The rescue fixed the shared isolation seam, not the timeout: sweep stale top-level `fn-test-home-*` roots with a bounded one-level prefix scan, reject inherited `HOME` values that do not live under the current `fusion-test-workers-*` root, recreate/remark the worker root before each `mkdtemp`, reset module/singleton fixture state in the affected suites, close real stores created by research helpers, and narrow slow real-store seams by moving package imports out of timed test bodies. When rescuing a similar CLI batch, prove it with repeated rescued-file runs plus `pnpm --filter @runfusion/fusion test`, audit rescued files for `vi.setConfig`/`testTimeout`/`hookTimeout` appeasement, and keep ledger/config removals in the same commit. +**Non-CLI quarantine sweep pattern (FN-6433):** for engine/core/dashboard batches, first remove quarantine excludes only in temporary local configs and run the exact quarantined files together so suite-load coupling is visible before editing the ledger. Rescue is valid when the grouped package lane proves the invariant now holds (for example, FN-6433 fixed engine cross-file interference by replacing broad `activeSessionRegistry.clear()` cleanup with path-scoped unregistering) or when a prior shared-fixture fix is demonstrated under package load. Delete duplicate/low-value files under the ratchet when another deterministic suite owns the same invariant. Finish by making `scripts/lib/test-quarantine.json` and every package Vitest exclude array converge in one commit, then prove the empty/non-empty state with package lanes, `pnpm test:gate`, `pnpm test`, `pnpm build`, and the bounded temp-leak output from `pnpm test`. + **Gate eviction:** a flake inside the merge gate cannot block all merges while red — it is evicted by removing its line from the `engine-core` allow-list (no quarantine entry needed unless it should also leave the non-blocking tier). **Gate admission:** the mirror operation — add the test's path to the `engine-core` `include` array in `packages/engine/vitest.config.ts`, citing the evidence of value (a real regression it caught) in the PR. Keep the project under its ~60s wall-clock budget. diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts index 4fcee89a8c..4fe1b94b39 100644 --- a/packages/core/vitest.config.ts +++ b/packages/core/vitest.config.ts @@ -7,13 +7,11 @@ const maxWorkers = computeMaxWorkers(); const quarantinedCoreTests = [ /* FNXC:CoreTests 2026-06-13-17:43: - The full workspace suite must not fail on suite-load-sensitive tests that pass standalone or only fail after excessive wall time. Quarantine the observed core offenders after package-lane hook timeouts instead of appeasing them with wider hook timeouts. + The full workspace suite must not fail on suite-load-sensitive tests that pass standalone or only fail after excessive wall time. Quarantine observed core offenders after package-lane hook timeouts instead of appeasing them with wider hook timeouts. + + FNXC:CoreTests 2026-06-14-02:14: + FN-6433 re-ran the core quarantine batch after FN-6430's shared fixture cleanup and rescued all five files without timeout or assertion changes. Keep this array empty unless a future quarantine is mirrored in scripts/lib/test-quarantine.json in the same commit. */ - "src/__tests__/db.test.ts", - "src/__tests__/run-audit.integration.test.ts", - "src/__tests__/run-audit.test.ts", - "src/__tests__/store-handoff-to-review.test.ts", - "src/__tests__/todo-store.test.ts", ]; export default defineConfig({ diff --git a/packages/dashboard/vitest.config.ts b/packages/dashboard/vitest.config.ts index 13889c4d55..2213e45934 100644 --- a/packages/dashboard/vitest.config.ts +++ b/packages/dashboard/vitest.config.ts @@ -234,16 +234,14 @@ const qualityAppSettingsOnlyTests = ["app/components/__tests__/SettingsModal.tes const quarantinedDashboardTests: string[] = [ /* FNXC:Testing 2026-06-13-18:05: - Full dashboard API quality runs exposed suite-load-sensitive failures in process-group timeout and git branch-commit route tests, while both files passed standalone immediately afterward. - FN-6416 requires the heap wrapper test to stay excluded during the 14-day deletion-ratchet window instead of widening waits or weakening assertions. + Full dashboard API quality runs exposed suite-load-sensitive failures in process-group timeout and git branch-commit route tests, while both files passed standalone immediately afterward. FN-6416 required exclusion during the 14-day deletion-ratchet window instead of widening waits or weakening assertions. FNXC:DashboardTests 2026-06-14-00:43: - Vitest project entries must apply the same quarantine list as the exported dashboardQualityProjectGlobs inventory. - Some projects define their own exclude arrays, so each runnable project includes these entries explicitly instead of relying on top-level inheritance. + Vitest project entries must apply the same quarantine list as the exported dashboardQualityProjectGlobs inventory. Some projects define their own exclude arrays, so each runnable project includes these entries explicitly instead of relying on top-level inheritance. + + FNXC:DashboardTests 2026-06-14-02:24: + FN-6433 rescued the dashboard quarantine batch after unquarantined app-backfill and API-quality runs passed with no assertion or timeout changes. Keep this array empty unless a future dashboard quarantine is mirrored in scripts/lib/test-quarantine.json in the same commit. */ - "app/components/__tests__/QuickEntryBox.test.tsx", - "scripts/__tests__/run-vitest-with-heap.test.ts", - "src/__tests__/routes-git.test.ts", ]; const qualityApiTests = [ diff --git a/packages/engine/src/__tests__/merger-ai-cleanup-active-session.test.ts b/packages/engine/src/__tests__/merger-ai-cleanup-active-session.test.ts index 440cd94166..63c586d7ab 100644 --- a/packages/engine/src/__tests__/merger-ai-cleanup-active-session.test.ts +++ b/packages/engine/src/__tests__/merger-ai-cleanup-active-session.test.ts @@ -8,10 +8,18 @@ import { MIN_TEMP_WORKTREE_REAP_AGE_MS } from "../self-healing.js"; import type { RunAuditor } from "../run-audit.js"; const tracked = new Set(); +const registeredActivePaths = new Set(); const RM = { recursive: true, force: true, maxRetries: 5, retryDelay: 50 } as const; afterEach(() => { - activeSessionRegistry.clear(); + /* + FNXC:EngineTests 2026-06-14-02:09: + Engine test files run in parallel and share the active-session singleton. Cleanup must unregister only paths registered by this file so one AI-merge cleanup test cannot erase another file's live session assertion under package load. + */ + for (const path of registeredActivePaths) { + activeSessionRegistry.unregisterPath(path); + } + registeredActivePaths.clear(); for (const dir of tracked) { try { rmSync(dir, RM); } catch { /* best effort */ } } @@ -51,18 +59,20 @@ function makeAge(path: string, ageMs: number): void { describe("AI merge active-session pruning", () => { it("pruneExistingAiMergeWorktrees skips active-session paths", async () => { const projectRoot = tempProjectRoot(); - const stale = tempAiMergeDir(projectRoot, "fusion-ai-merge-fn-777-active"); + const stale = tempAiMergeDir(projectRoot, "fusion-ai-merge-fn-779-active"); const canonical = realpathSync(stale); - activeSessionRegistry.registerPath(canonical, { taskId: "FN-777", kind: "ai-merge", ownerKey: "ai-merge:FN-777:attempt-1" }); + activeSessionRegistry.registerPath(canonical, { taskId: "FN-779", kind: "ai-merge", ownerKey: "ai-merge:FN-779:attempt-1" }); + registeredActivePaths.add(canonical); const { audit, events } = makeAudit(); - await expect(pruneExistingAiMergeWorktrees("FN-777", projectRoot, audit, vi.fn(async () => undefined))).resolves.toBe(0); + await expect(pruneExistingAiMergeWorktrees("FN-779", projectRoot, audit, vi.fn(async () => undefined))).resolves.toBe(0); expect(existsSync(stale)).toBe(true); expect(events).toEqual([]); activeSessionRegistry.unregisterPath(canonical); + registeredActivePaths.delete(canonical); makeAge(stale, MIN_TEMP_WORKTREE_REAP_AGE_MS + 1_000); - await expect(pruneExistingAiMergeWorktrees("FN-777", projectRoot, audit, vi.fn(async () => undefined))).resolves.toBe(1); + await expect(pruneExistingAiMergeWorktrees("FN-779", projectRoot, audit, vi.fn(async () => undefined))).resolves.toBe(1); expect(existsSync(stale)).toBe(false); }); }); diff --git a/packages/engine/src/__tests__/merger-ai-cleanup.test.ts b/packages/engine/src/__tests__/merger-ai-cleanup.test.ts index ab72637436..09358b660a 100644 --- a/packages/engine/src/__tests__/merger-ai-cleanup.test.ts +++ b/packages/engine/src/__tests__/merger-ai-cleanup.test.ts @@ -30,7 +30,10 @@ const RM = { recursive: true, force: true, maxRetries: 5, retryDelay: 50 } as co afterEach(() => { vi.restoreAllMocks(); fsState.failReaddirPath = ""; - activeSessionRegistry.clear(); + /* + FNXC:EngineTests 2026-06-14-02:10: + This file observes AI-merge active-session state while sibling files may also be asserting live registrations. Do not clear the shared registry here; production cleanup paths must unregister their own entries, and broad singleton clears make package-load rescue nondeterministic. + */ for (const dir of tracked) { try { rmSync(dir, RM); } catch { /* best effort */ } } diff --git a/packages/engine/src/__tests__/reliability-interactions/soft-delete-blocker-residue.test.ts b/packages/engine/src/__tests__/reliability-interactions/soft-delete-blocker-residue.test.ts deleted file mode 100644 index f652b4dee9..0000000000 --- a/packages/engine/src/__tests__/reliability-interactions/soft-delete-blocker-residue.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; - -import { DEFAULT_SETTINGS, TaskStore, type Task } from "@fusion/core"; -import { afterEach, describe, expect, it, vi } from "vitest"; - -import { Scheduler } from "../../scheduler.js"; -import { SelfHealingManager } from "../../self-healing.js"; - -type Fixture = { rootDir: string; store: TaskStore; scheduler: Scheduler; selfHealing: SelfHealingManager }; - -async function createFixture(autoMerge = true): Promise { - const rootDir = await mkdtemp(join(tmpdir(), "fusion-fn5566-")); - await mkdir(join(rootDir, ".fusion"), { recursive: true }); - await writeFile(join(rootDir, "README.md"), "# test\n", "utf8"); - const store = new TaskStore(rootDir, undefined, { inMemoryDb: true }); - await store.init(); - await store.updateSettings({ ...DEFAULT_SETTINGS, autoMerge } as any); - const scheduler = new Scheduler(store as any); - const selfHealing = new SelfHealingManager(store, { rootDir, getExecutingTaskIds: () => new Set() }); - return { rootDir, store, scheduler, selfHealing }; -} - -async function createTask(store: TaskStore, input: Partial): Promise { - return store.createTask({ title: "task", description: "task", prompt: "## File Scope\n- packages/engine/src/**\n", steps: [], ...input } as any); -} - -describe("reliability interactions: FN-5566 / FN-5446 soft-delete blocker residue", () => { - const fixtures: Fixture[] = []; - afterEach(async () => { - while (fixtures.length) { - const fx = fixtures.pop()!; - fx.scheduler.stop(); - fx.selfHealing.stop(); - fx.store.close(); - await rm(fx.rootDir, { recursive: true, force: true }); - } - }); - - it("covers direct-delete blocker residue and blockedBy-only paths", async () => { - const fx = await createFixture(); - fixtures.push(fx); - const blocker = await createTask(fx.store, { column: "todo" }); - const other = await createTask(fx.store, { column: "todo" }); - const depA = await createTask(fx.store, { column: "todo", status: "blocked", dependencies: [blocker.id], blockedBy: blocker.id }); - const depB = await createTask(fx.store, { column: "todo", status: "blocked", dependencies: [other.id], blockedBy: blocker.id }); - - await fx.store.deleteTask(blocker.id, { removeDependencyReferences: true }); - - const depAAfter = await fx.store.getTask(depA.id); - const depBAfter = await fx.store.getTask(depB.id); - expect(depAAfter.blockedBy ?? null).toBeNull(); - expect(depAAfter.status ?? null).toBeNull(); - expect(depAAfter.dependencies).not.toContain(blocker.id); - expect(depBAfter.blockedBy ?? null).toBeNull(); - expect(depBAfter.status ?? null).toBeNull(); - expect(depBAfter.dependencies).toEqual([other.id]); - }); - - it("event-driven reconciliation reblocks dependents to next unresolved dependency", async () => { - const fx = await createFixture(); - fixtures.push(fx); - const blocker = await createTask(fx.store, { column: "in-progress" }); - const other = await createTask(fx.store, { column: "todo" }); - const dep = await createTask(fx.store, { column: "todo", status: "blocked", blockedBy: blocker.id, dependencies: [other.id, blocker.id] }); - - const now = new Date().toISOString(); - const db = fx.store.getDatabase(); - db.prepare("UPDATE tasks SET deletedAt = ?, \"column\" = 'archived', updatedAt = ? WHERE id = ?").run(now, now, blocker.id); - fx.store.emit("task:deleted", await fx.store.getTask(blocker.id, { includeDeleted: true })); - - await vi.waitFor(async () => { - const depAfter = await fx.store.getTask(dep.id); - expect(depAfter.blockedBy).toBe(other.id); - expect(depAfter.status).toBe("queued"); - }); - }); - - it("reconciles soft-delete column drift with audit and preserves FN-5208 invariants", async () => { - const fx = await createFixture(); - fixtures.push(fx); - const drift = await createTask(fx.store, { column: "in-review" }); - await fx.store.deleteTask(drift.id); - const db = fx.store.getDatabase(); - db.prepare("UPDATE tasks SET \"column\" = 'in-review' WHERE id = ?").run(drift.id); - - const first = await fx.selfHealing.reconcileSoftDeletedColumnDrift(); - const second = await fx.selfHealing.reconcileSoftDeletedColumnDrift(); - const row = db.prepare("SELECT deletedAt, \"column\" as column, allowResurrection FROM tasks WHERE id = ?").get(drift.id) as any; - - expect(first.reconciled).toBe(1); - expect(second.reconciled).toBe(0); - expect(row.column).toBe("archived"); - expect(row.deletedAt).toBeTruthy(); - expect(row.allowResurrection).toBe(0); - const auditEvents = (fx.store as any).getRunAuditEvents({ mutationType: "task:soft-delete-column-reconciled", limit: 10 }) as any[]; - expect(auditEvents).toHaveLength(1); - }); - - it("clearStaleBlockedBy handles missed task:deleted event with soft-deleted-blocker reason", async () => { - const fx = await createFixture(); - fixtures.push(fx); - const blocker = await createTask(fx.store, { column: "todo" }); - const dep = await createTask(fx.store, { column: "todo", status: "blocked", blockedBy: blocker.id, dependencies: [] }); - - await fx.store.deleteTask(blocker.id, { removeDependencyReferences: true }); - await fx.store.updateTask(dep.id, { blockedBy: blocker.id, status: "blocked" as any }); - - await fx.selfHealing.clearStaleBlockedBy(); - const depAfter = await fx.store.getTask(dep.id); - expect(depAfter.blockedBy ?? null).toBeNull(); - expect( - depAfter.log.some((entry) => entry.action.includes("soft-deleted") || entry.action.includes("reason=soft-deleted-blocker")), - ).toBe(true); - }); - - it("FN-5147 composition: live in-review tasks remain untouched when autoMerge=false", async () => { - const fx = await createFixture(false); - fixtures.push(fx); - const live = await createTask(fx.store, { column: "in-review", status: "failed" }); - - const result = await fx.selfHealing.reconcileSoftDeletedColumnDrift(); - const liveAfter = await fx.store.getTask(live.id); - expect(result.reconciled).toBe(0); - expect(liveAfter.column).toBe("in-review"); - }); -}); diff --git a/packages/engine/vitest.config.ts b/packages/engine/vitest.config.ts index ac3bacc300..20a0b713e4 100644 --- a/packages/engine/vitest.config.ts +++ b/packages/engine/vitest.config.ts @@ -102,9 +102,10 @@ export default defineConfig({ "src/**/*.slow.test.ts", "node_modules/**", "dist/**", - "src/__tests__/merger-ai-cleanup-active-session.test.ts", - "src/__tests__/merger-ai-cleanup.test.ts", - "src/__tests__/merger-ai.test.ts", + /* + FNXC:EngineTests 2026-06-14-02:11: + FN-6433 rescued the AI-merge suites by replacing broad activeSessionRegistry cleanup with path-scoped cleanup, so the default engine lane should execute them again. The soft-delete blocker residue suite was deleted under the ratchet because deterministic soft-delete deadlock coverage already owns that invariant. + */ ], }, }, @@ -117,7 +118,10 @@ export default defineConfig({ // also tier into engine-slow. exclude: [ "src/**/*.slow.test.ts", - "src/__tests__/reliability-interactions/soft-delete-blocker-residue.test.ts", + /* + FNXC:EngineTests 2026-06-14-02:12: + FN-6433 removed the reliability-interactions quarantine after deleting the duplicate soft-delete blocker residue file under the deletion ratchet; keep this project exclude list ledger-free unless a new flake is quarantined in lockstep. + */ ], // These tests assert event ordering across real worktrees. Parallel // execution under merger load caused subprocess-guard timeouts and diff --git a/scripts/lib/test-quarantine.json b/scripts/lib/test-quarantine.json index 7dd9fcfa13..2439bcba67 100644 --- a/scripts/lib/test-quarantine.json +++ b/scripts/lib/test-quarantine.json @@ -1,65 +1,4 @@ { - "$comment": "Flaky-test quarantine ledger (deletion ratchet — see AGENTS.md 'Flaky tests: quarantine on sight' and docs/testing.md 'Quarantine ledger and the deletion ratchet'). A test observed failing without a corresponding real bug is quarantined ON SIGHT: add an entry here AND a matching one-line `exclude` entry in that package's vitest config, in the same commit. Every entry needs a non-empty `reason` (link the failing run) and a `quarantinedAt` date — the entry expires 14 days later, at which point the test file is DELETED unless someone rescues it with evidence it catches real regressions plus a root-cause fix (never appeasement). There is deliberately no loader module and no automation around this file: it is a dated record, the vitest config exclude is the mechanism, and the sweep is policy executed by whoever touches the suite.", - "entries": [ - { - "file": "packages/engine/src/__tests__/merger-ai-cleanup-active-session.test.ts", - "reason": "Flake: pruneExistingAiMergeWorktrees skips active-session paths — active-session temp AI merge dir was unexpectedly pruned during pnpm --filter @fusion/engine test in FN-6206 verification, while the same file passed standalone. Root cause suspected: realpathSync resolution mismatch or readdirSync mock interaction with activeSessionRegistry singleton under concurrent engine suite load. Discovered during FN-6206.", - "quarantinedAt": "2026-06-10" - }, - { - "file": "packages/engine/src/__tests__/merger-ai-cleanup.test.ts", - "reason": "Flake observed during FN-6206 verification: `pruneExistingAiMergeWorktrees skips active-session paths` failed in full `pnpm --filter @fusion/engine test` runs while the file passed standalone, indicating suite-order/concurrency sensitivity. Follow-up FN-6207.", - "quarantinedAt": "2026-06-10" - }, - { - "file": "packages/engine/src/__tests__/merger-ai.test.ts", - "reason": "Flake observed during FN-6238 verification: full `pnpm --filter @fusion/engine test` failed in two merger-ai tests with git ENOENT / unable to read current working directory after a temp checkout disappeared, while the file passed standalone (23/23). Follow-up FN-6248.", - "quarantinedAt": "2026-06-11" - }, - { - "file": "packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx", - "reason": "Flake observed during FN-6239 verification: broad `pnpm test` in dashboard backfill shard 4/4 could not find `quick-entry-priority-button` immediately after a successful task creation, while the named test passed standalone. Indicates suite-order/concurrency sensitivity unrelated to QuickChatFAB coverage.", - "quarantinedAt": "2026-06-11" - }, - { - "file": "packages/engine/src/__tests__/reliability-interactions/soft-delete-blocker-residue.test.ts", - "reason": "Flake observed during FN-6294 verification and reproduced during FN-6319 broad `pnpm --filter @fusion/engine test`: `clearStaleBlockedBy handles missed task:deleted event with soft-deleted-blocker reason` failed because the log entry was absent, while the same file passed standalone and the narrow three-file reproduction passed. Product-code cross-check: `clearStaleBlockedBy` still has the soft-deleted-blocker branch and soft-delete-deadlock-scan-exclusion.test.ts covers it via a deterministic store double, indicating suite-order/concurrency sensitivity in this reliability-interactions fixture rather than a confirmed product bug.", - "quarantinedAt": "2026-06-12" - }, - { - "file": "packages/core/src/__tests__/store-handoff-to-review.test.ts", - "reason": "Flake observed during full workspace verification on 2026-06-13: `pnpm test:full` failed in `TaskStore handoffToReview > audits direct moveTask in-review transitions as invariant violations` because the test file's `beforeEach` exceeded the 15s core hook timeout under recursive full-suite load. The same file passed standalone immediately afterward (`pnpm --filter @fusion/core exec vitest run src/__tests__/store-handoff-to-review.test.ts --silent=passed-only --reporter=dot`, 8/8), so this is suite-load/concurrency sensitivity rather than a confirmed product bug. Quarantined instead of widening hook timeouts.", - "quarantinedAt": "2026-06-13" - }, - { - "file": "packages/core/src/__tests__/db.test.ts", - "reason": "Slow/flaky core suite observed during 2026-06-13 verification: full core package runs timed out in `Database > change detection > bumpLastModified strictly increases the timestamp` beforeEach under the 15s hook timeout. A direct file run also exposed a real `Database.recoverIfCorrupt` failed-swap preservation bug, which was fixed separately; the file remains a 176s standalone slow offender and its hook timeout is suite-load sensitivity, so it is quarantined rather than appeased with broader hook timeouts.", - "quarantinedAt": "2026-06-13" - }, - { - "file": "packages/core/src/__tests__/run-audit.integration.test.ts", - "reason": "Slow/flaky core suite observed during 2026-06-13 verification: `pnpm --filter @fusion/core test` timed out in `Run Audit Integration > multi-domain event correlation > round-trips sandbox domain events and filters by sandbox` beforeEach and then produced ENOTEMPTY cleanup fallout. The same file passed as a direct run (24/24) but took about 90s, indicating load-sensitive slow-test behavior rather than a confirmed product bug.", - "quarantinedAt": "2026-06-13" - }, - { - "file": "packages/core/src/__tests__/run-audit.test.ts", - "reason": "Slow/flaky core suite observed during 2026-06-13 verification: `pnpm --filter @fusion/core test` timed out in `Run Audit > recordRunAuditEvent > records a basic audit event with required fields` beforeEach and then produced ENOTEMPTY cleanup fallout. The same file passed as a direct run (28/28) but took about 96s, indicating load-sensitive slow-test behavior rather than a confirmed product bug.", - "quarantinedAt": "2026-06-13" - }, - { - "file": "packages/core/src/__tests__/todo-store.test.ts", - "reason": "Slow/flaky core suite observed during 2026-06-13 verification: `pnpm --filter @fusion/core test` timed out in `TodoStore > list CRUD > listLists returns lists ordered by createdAt and scoped by project` beforeEach. The same file passed as a direct run (18/18) but took about 48s, indicating load-sensitive slow-test behavior rather than a confirmed product bug.", - "quarantinedAt": "2026-06-13" - }, - { - "file": "packages/dashboard/scripts/__tests__/run-vitest-with-heap.test.ts", - "reason": "FN-6416 quarantine: flake observed during FN-6411 `pnpm test` dashboard api:curated lane on 2026-06-13: `run-vitest-with-heap > times out and reaps the spawned process group` failed waiting for its stub process tree within 5000ms under full dashboard API load and left `fusion-test-workers-*` temp-worker roots for bounded cleanup. The same test passed standalone immediately afterward (`pnpm --filter @fusion/dashboard exec vitest run --project dashboard-api-quality scripts/__tests__/run-vitest-with-heap.test.ts -t \"times out and reaps the spawned process group\" --silent=passed-only --reporter=dot`, 1/1), indicating timing/concurrency sensitivity. Quarantined instead of widening wait timeouts.", - "quarantinedAt": "2026-06-13" - }, - { - "file": "packages/dashboard/src/__tests__/routes-git.test.ts", - "reason": "Flake observed during `pnpm test` dashboard api:curated lane on 2026-06-13: `Git Management endpoints > GET /git/branches/:name/commits > respects limit parameter` returned 400 instead of 200 under concurrent dashboard API tests. The same filtered file passed standalone immediately afterward (`pnpm --filter @fusion/dashboard exec vitest run --project dashboard-api-quality src/__tests__/routes-git.test.ts -t \"respects limit parameter\" --silent=passed-only --reporter=dot`, 3/3), indicating suite-load or fixture-state sensitivity rather than a confirmed product bug. Quarantined instead of loosening assertions.", - "quarantinedAt": "2026-06-13" - } - ] + "$comment": "Flaky-test quarantine ledger (deletion ratchet \u2014 see AGENTS.md 'Flaky tests: quarantine on sight' and docs/testing.md 'Quarantine ledger and the deletion ratchet'). A test observed failing without a corresponding real bug is quarantined ON SIGHT: add an entry here AND a matching one-line `exclude` entry in that package's vitest config, in the same commit. Every entry needs a non-empty `reason` (link the failing run) and a `quarantinedAt` date \u2014 the entry expires 14 days later, at which point the test file is DELETED unless someone rescues it with evidence it catches real regressions plus a root-cause fix (never appeasement). There is deliberately no loader module and no automation around this file: it is a dated record, the vitest config exclude is the mechanism, and the sweep is policy executed by whoever touches the suite.", + "entries": [] }