FN-7068: rescue quarantined flaky tests

Rescue quarantined dashboard and engine tests before the deletion deadline.

- Realign the DevServerView mobile CSS test with split mobile rules and balanced at-rule extraction.
- Complete FN-5488 self-healing TaskStore fakes for overlap-scope paths.
- Remove rescued dashboard and engine tests from quarantine configs and the ledger.

Files changed:
 .../__tests__/DevServerView.mobile.test.tsx        | 74 ++++++++++++++++++----
 packages/dashboard/vitest.config.ts                |  8 +--
 ...in-review-merge-stall-deadlock-recovery.test.ts |  7 ++
 ...f-healing-fn-5488-fast-path-regressions.test.ts |  6 ++
 packages/engine/vitest.config.ts                   |  8 +--
 scripts/lib/test-quarantine.json                   | 18 +-----
 6 files changed, 79 insertions(+), 42 deletions(-)

Fusion-Task-Id: FN-7068

Fusion-Task-Lineage: b40a551f-8981-49a1-86a3-660f08cceb94
This commit is contained in:
gsxdsm
2026-06-26 13:32:35 -07:00
parent 31d3f21e18
commit 0afc66b6be
6 changed files with 78 additions and 41 deletions

View File

@@ -23,6 +23,46 @@ vi.mock("../DevServerLogViewer", () => ({
DevServerLogViewer: () => <div data-testid="mock-devserver-log-viewer" />,
}));
function extractAtRuleBlocks(css: string, marker: string): string[] {
const blocks: string[] = [];
let searchFrom = 0;
while (searchFrom < css.length) {
const start = css.indexOf(marker, searchFrom);
if (start === -1) break;
const open = css.indexOf("{", start);
if (open === -1) break;
let depth = 1;
let cursor = open + 1;
while (cursor < css.length && depth > 0) {
if (css[cursor] === "{") depth++;
else if (css[cursor] === "}") depth--;
cursor++;
}
blocks.push(css.slice(open + 1, cursor - 1));
searchFrom = cursor;
}
return blocks;
}
function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function selectorRule(css: string, selector: string): string | null {
const escapedSelector = escapeRegExp(selector);
const match = css.match(new RegExp(`(?:^|\\n)\\s*${escapedSelector}\\s*\\{[\\s\\S]*?\\n\\s*\\}`, "m"));
return match?.[0] ?? null;
}
function countSelectorRules(css: string, selector: string): number {
const escapedSelector = escapeRegExp(selector);
return (css.match(new RegExp(`(?:^|\\n)\\s*${escapedSelector}\\s*\\{`, "g")) ?? []).length;
}
function createDevServerHookState() {
return {
session: {
@@ -48,25 +88,31 @@ function createDevServerHookState() {
describe("DevServerView mobile CSS/structure", () => {
it("defines one mobile rule-set for preview header/actions and wraps badge correctly", () => {
const css = loadAllAppCss();
const mobileBlockMatch = css.match(/@media[^{]*\(max-width: 768px\)[^{]*\{([\s\S]*?)\n\}/g) ?? [];
const mobileCss = mobileBlockMatch.join("\n");
/*
FNXC:DashboardTests 2026-06-26-13:05:
DevServerView intentionally keeps preview-header and modal-launcher copy mobile wrapping in separate rules so each surface can be asserted directly. Extract the balanced viewport at-rule from the loaded app CSS instead of counting a stale grouped selector that drifted after the CSS was split.
*/
const mobileCss = extractAtRuleBlocks(css, "@media (max-width: 768px)")
.find((block) => block.includes(".devserver-preview-header") && block.includes(".devserver-preview-modal-launcher__copy"));
const headerRuleCount = (mobileCss.match(/\.devserver-preview-header,\s*\.devserver-preview-modal-launcher__copy\s*\{/g) ?? []).length;
expect(headerRuleCount).toBe(1);
expect(mobileCss).toMatch(/\.devserver-preview-url-badge\s*\{[\s\S]*max-width:\s*100%/);
expect(mobileCss).toMatch(/\.dev-server-header-title\s*\{[\s\S]*flex-wrap:\s*wrap/);
expect(mobileCss).toBeTruthy();
expect(countSelectorRules(mobileCss ?? "", ".devserver-preview-header")).toBe(1);
expect(selectorRule(mobileCss ?? "", ".devserver-preview-header")).toMatch(/flex-wrap:\s*wrap/);
expect(countSelectorRules(mobileCss ?? "", ".devserver-preview-modal-launcher__copy")).toBe(1);
expect(selectorRule(mobileCss ?? "", ".devserver-preview-modal-launcher__copy")).toMatch(/flex-wrap:\s*wrap/);
expect(selectorRule(mobileCss ?? "", ".devserver-preview-url-badge")).toMatch(/max-width:\s*100%/);
expect(selectorRule(mobileCss ?? "", ".dev-server-header-title")).toMatch(/flex-wrap:\s*wrap/);
});
it("defines narrow right-dock launcher and modal rules without duplicating mobile media rules", () => {
const css = loadAllAppCss();
const containerStart = css.indexOf("@container right-dock-body (max-width: 768px)");
expect(containerStart).toBeGreaterThan(-1);
const containerCss = css.slice(containerStart);
const containerCss = extractAtRuleBlocks(css, "@container right-dock-body (max-width: 768px)")[0];
expect(containerCss).toBeTruthy();
expect(containerCss).toMatch(/\.devserver-preview-panel,\s*\.devserver-preview-modal-launcher\s*\{[\s\S]*grid-column:\s*auto/);
expect(containerCss).toMatch(/\.devserver-preview-modal\s*\{[\s\S]*width:\s*min\(calc\(var\(--space-2xl\) \* 20\), calc\(100vw - var\(--space-md\) \* 2\)\)/);
expect(containerCss).toMatch(/\.devserver-preview-panel \.devserver-preview-container/);
expect(containerCss).not.toMatch(/\.dev-server-logs,\s*\.devserver-preview-container,\s*\.devserver-preview-iframe/);
expect(containerCss ?? "").toMatch(/\.devserver-preview-panel,\s*\.devserver-preview-modal-launcher\s*\{[\s\S]*grid-column:\s*auto/);
expect(containerCss ?? "").toMatch(/\.devserver-preview-modal\s*\{[\s\S]*width:\s*min\(calc\(var\(--space-2xl\) \* 20\), calc\(100vw - var\(--space-md\) \* 2\)\)/);
expect(containerCss ?? "").toMatch(/\.devserver-preview-panel \.devserver-preview-container/);
expect(containerCss ?? "").not.toMatch(/\.dev-server-logs,\s*\.devserver-preview-container,\s*\.devserver-preview-iframe/);
expect(css).toMatch(/@media[^{]*\(max-width: 768px\)/);
expect(css).toMatch(/@container right-dock-body \(max-width: 768px\)/);

View File

@@ -320,12 +320,10 @@ FNXC:DashboardTestQuarantine 2026-06-22-18:05:
FN-6937 verified that FN-6860's claimed session-cross-tab ledger removal had not landed: the file was active because this exclude list was empty, but `test-quarantine.json` still carried the stale 2026-06-19 row. The repeated loaded `dashboard-api-quality-backfill` runs and lock-holder mutation proof confirmed FN-6742's rescue still holds, so remove the orphaned ledger row and keep this list empty to restore ledger↔config lockstep.
*/
/*
FNXC:DashboardTestQuarantine 2026-06-25-09:50:
Quarantine DevServerView.mobile.test.tsx: CI full-suite shard 4/4 fails with 'expected +0 to be 1' on the mobile CSS structure assertion. Under the deletion ratchet — see scripts/lib/test-quarantine.json.
FNXC:DashboardTestQuarantine 2026-06-26-13:16:
FN-7068 rescued DevServerView.mobile before the deletion deadline by realigning its mobile CSS assertion to the split preview-header and launcher-copy rules in DevServerView.css. Keep this quarantine list empty until a new flaky dashboard file is added with a matching ledger entry.
*/
const quarantinedDashboardTests: string[] = [
"app/components/__tests__/DevServerView.mobile.test.tsx",
];
const quarantinedDashboardTests: string[] = [];
const qualityApiTests = [
// Critical HTTP/server behavior: auth, task/project/settings mutation,

View File

@@ -40,6 +40,13 @@ describe("SelfHealingManager in-review merge stall deadlock recovery (FN-5488)",
if (!opts?.column) return all;
return all.filter((task) => task.column === opts.column);
}),
getTask: vi.fn().mockImplementation(async (id: string) => tasks.get(id) ?? null),
/*
FNXC:OverlapSelfHealing 2026-06-26-12:53:
The FN-5488 stale-blockedBy sweep now reads overlap scopes, soft-deleted blockers, and completion-handoff markers through TaskStore. Keep this fake in lockstep with every clearStaleBlockedBy() store seam so the merge-stall recovery counts stay deterministic across isolated and full-suite shard runs.
*/
parseFileScopeFromPrompt: vi.fn().mockResolvedValue(["packages/engine/src/self-healing.ts"]),
getCompletionHandoffAcceptedMarker: vi.fn().mockReturnValue(null),
updateTask: vi.fn().mockImplementation(async (id: string, patch: Partial<Task>) => {
const current = tasks.get(id);
if (!current) throw new Error(`Task ${id} missing`);

View File

@@ -46,6 +46,12 @@ function makeStore(tasksInput: Task[]) {
return all.filter((task) => task.column === opts.column);
}),
getTask: vi.fn().mockImplementation(async (id: string) => tasks.get(id) ?? null),
/*
FNXC:OverlapSelfHealing 2026-06-26-12:52:
The FN-5488 overlap path calls every TaskStore seam in clearStaleBlockedBy(), and these fakes must stay complete so full-suite shard ordering cannot turn overlap-preservation invariants into fake drift. Use a non-empty shared scope so hasActiveFileScopeOverlapBlocker reaches pathsOverlap instead of short-circuiting before the branch this file guards.
*/
parseFileScopeFromPrompt: vi.fn().mockResolvedValue(["packages/engine/src/self-healing.ts"]),
getCompletionHandoffAcceptedMarker: vi.fn().mockReturnValue(null),
updateTask: vi.fn().mockImplementation(async (id: string, patch: Partial<Task>) => {
const current = tasks.get(id);
if (!current) throw new Error(`Task ${id} missing`);

View File

@@ -120,13 +120,9 @@ export default defineConfig({
// / `test:all` invoked from the root `test:full` script.
"src/**/*.slow.test.ts",
/*
FNXC:EngineTests 2026-06-25-09:50:
Quarantine self-healing-fn-5488-fast-path-regressions.test.ts: CI full-suite shard 1/4 fails with 'expected +0 to be 1' and 'this.store.parseFileScopeFromPrompt is not a function'. No corresponding source bug in recent changes.
Quarantine in-review-merge-stall-deadlock-recovery.test.ts: CI full-suite shard 2/4 fails with 'expected FN-5485 to be null'. Self-healing FN-5488 overlap blocker behavior is flaky under CI load.
Both under the deletion ratchet — see scripts/lib/test-quarantine.json.
FNXC:EngineTests 2026-06-26-13:15:
FN-7068 rescued the 2026-06-25 self-healing quarantine batch by completing the local TaskStore fakes for the FN-5488 overlap path. Keep both files active in engine-default so fake drift around clearStaleBlockedBy() is caught before the deletion ratchet expires.
*/
"src/__tests__/self-healing-fn-5488-fast-path-regressions.test.ts",
"src/__tests__/in-review-merge-stall-deadlock-recovery.test.ts",
/*
FNXC:EngineTests 2026-06-16-19:05:
FN-6492 verification caught cli-agent-executor as a package-lane-only flake: the hard-cancel assertion failed once and left an ENOTEMPTY temp hook directory, then the file passed in isolation. Quarantine the whole file under the deletion ratchet instead of weakening timing or process assertions.

View File

@@ -1,20 +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__/self-healing-fn-5488-fast-path-regressions.test.ts",
"reason": "Failing in CI full-suite shard 1/4: 'expected +0 to be 1' + 'this.store.parseFileScopeFromPrompt is not a function'. Run: https://github.com/Runfusion/Fusion/actions/runs/28206337202",
"quarantinedAt": "2026-06-25"
},
{
"file": "packages/engine/src/__tests__/in-review-merge-stall-deadlock-recovery.test.ts",
"reason": "Failing in CI full-suite shard 2/4: 'expected FN-5485 to be null'. Run: https://github.com/Runfusion/Fusion/actions/runs/28206337202",
"quarantinedAt": "2026-06-25"
},
{
"file": "packages/dashboard/app/components/__tests__/DevServerView.mobile.test.tsx",
"reason": "Failing in CI full-suite shard 4/4: 'expected +0 to be 1' (mobile CSS structure assertion). Run: https://github.com/Runfusion/Fusion/actions/runs/28206337202",
"quarantinedAt": "2026-06-25"
}
]
"entries": []
}