From e7a873c50507a87b975dd81fdbed7117b5db6b67 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 9 Aug 2026 23:06:18 -0700 Subject: [PATCH] FN-8936: stabilize Planning Mode handoff tests Stabilize live Proceed-action handoffs and re-admit the Planning Mode flow suite. - Settle hydration and re-query the Proceed action before direct-create test clicks. - Remove the Planning Mode test quarantine and record its rescue in the testing ledger. Files changed: .../suite-only-flakes-observed-register.md | 4 ++++ docs/testing.md | 3 +++ .../PlanningModeModal.planning-flow.test.tsx | 20 ++++++++++++++++---- packages/dashboard/vitest.config.ts | 5 ----- scripts/lib/test-quarantine.json | 5 ----- 5 files changed, 23 insertions(+), 14 deletions(-) Fusion-Task-Id: FN-8936 Fusion-Task-Lineage: ed869b67-9394-458b-879c-54da0d7d327e Co-authored-by: Fusion (runfusion.ai) --- .../suite-only-flakes-observed-register.md | 4 ++++ docs/testing.md | 3 +++ .../PlanningModeModal.planning-flow.test.tsx | 20 +++++++++++++++---- packages/dashboard/vitest.config.ts | 5 ----- scripts/lib/test-quarantine.json | 5 ----- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/docs/solutions/test-failures/suite-only-flakes-observed-register.md b/docs/solutions/test-failures/suite-only-flakes-observed-register.md index 0e19c67612..3ec1cbf534 100644 --- a/docs/solutions/test-failures/suite-only-flakes-observed-register.md +++ b/docs/solutions/test-failures/suite-only-flakes-observed-register.md @@ -75,6 +75,8 @@ Seven tests failed in `plugin-runner.test.ts`, but only this one identity surviv The failure is unrelated to the mobile question footer: it exercises the completed-plan Proceed handoff, while FN-8757 changes only the active-question footer. The file retains substantial coverage, so this first sighting is recorded rather than quarantined; a second sighting requires the normal file-level quarantine. +**Superseded 2026-08-10 (FN-8936):** The second sighting moved the file to the deletion-ratchet ledger. Investigation classified the direct handoff as a detached test-node hydration race, not a product create-state race; the suite was rescued by settling hydration and re-querying the live Proceed action before every previously unsafe direct click. The ledger and Vitest exclusion were removed together after exact and loaded-file proof, without timeout/retry/assertion appeasement. + ## 5. Planning Mode mobile plan-tab selection - **File:** `packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx` @@ -88,6 +90,8 @@ The failure is unrelated to the mobile question footer: it exercises the complet The failure exercises the pre-existing mobile tab transition, while the task-creation fix changes the completed-plan Proceed handoff. The file retains substantial coverage, so this first sighting is recorded rather than quarantined; a second sighting requires the normal file-level quarantine. +**Suite re-admitted 2026-08-10 (FN-8936):** This first-sighting mobile observation did not receive a second failure. The shared file-level quarantine was removed only after the direct-handoff root cause was structurally fixed and the unexcluded loaded suite, including this mobile coverage, passed. + ## Common shape and unverified suspicion The first three entries are PostgreSQL-backed or PostgreSQL-suite-adjacent, pass in isolation, and appear only under full-suite parallelism. This points at shared database state between those test files rather than any one test. It is **unverified and uninvestigated**, not a diagnosis; do not infer a root-cause fix from this record. The Planning Mode entries are separate frontend timing observations. diff --git a/docs/testing.md b/docs/testing.md index 81c170312c..dc48f9a018 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -388,6 +388,9 @@ Flags: **2026-08-09 Kimi K3 disposition (FN-8900):** Rescued `register-model-routes-kimi-k3-supplemental.test.ts` before the 2026-08-15 deadline. The route test now consumes pi-ai 0.82.1's real bundled K3 catalog via a deterministic registry seam, retaining SDK-catalog regression signal and route merge/deduplication coverage without `ModelRuntime.create()` or live `ModelRegistry.refresh()`. The re-measurement recorded refresh samples of 224, 74, 75, 279, and 300047 ms (the last process took 300.50 s), so the ledger entry and dashboard Vitest exclusion were removed together rather than widening the unchanged 15 s budget or adding a retry. + +**2026-08-10 Planning Mode disposition (FN-8936):** Rescued `PlanningModeModal.planning-flow.test.tsx` before its 2026-08-20 deadline. Investigation found no product state-machine race: Proceed snapshots stable session/summary refs and takes its single-flight guard before create. The loaded failure was a test harness detached-node race when session hydration replaced an action-bar button returned by `findByRole`. All unsafe direct Proceed handoffs now settle hydration and query a live button before clicking, while retaining strict create arguments, task-created/onTaskCreated, desktop/mobile handoff, claim-retry, retry, and multi-task assertions. Exact and loaded-file runs passed, and the matching ledger entry and dashboard Vitest exclude were removed together without changing timeouts or adding retries. + ### Validate before excluding and preserve timeout budgets Capture **full runner output** before recording or filing a ledger entry—for example, tee it to a file. Never pipe a dot reporter through `tail`: the summary remains but the `FAIL` identity lines needed for evidence are truncated. diff --git a/packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx b/packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx index ca9e9546ba..c6a0cb097b 100644 --- a/packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx +++ b/packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx @@ -45,6 +45,18 @@ function answeredHistory(count: number) { })); } +/* +FNXC:PlanningMode 2026-08-10-05:45: +A resumed plan can finish hydration after its Proceed action first becomes discoverable, replacing +that action-bar node before an event dispatches. Settle the pending commit and query the live button +at click time so every direct-create handoff tests a real user action rather than a detached node. +*/ +async function clickProceedAfterHydration() { + await screen.findByRole("button", { name: "Proceed with plan" }); + await act(async () => {}); + fireEvent.click(screen.getByRole("button", { name: "Proceed with plan" })); +} + describe("PlanningModeModal sequential flow", () => { beforeEach(() => { vi.useRealTimers(); @@ -604,7 +616,7 @@ describe("PlanningModeModal sequential flow", () => { const onViewTask = vi.fn(); render(); - fireEvent.click(await screen.findByRole("button", { name: "Proceed with plan" })); + await clickProceedAfterHydration(); await waitFor(() => expect(mockCreateTaskFromPlanning).toHaveBeenCalledWith( "session-1", @@ -716,7 +728,7 @@ describe("PlanningModeModal sequential flow", () => { const onViewTask = vi.fn(); render(); - fireEvent.click(await screen.findByRole("button", { name: "Proceed with plan" })); + await clickProceedAfterHydration(); expect(await screen.findByRole("button", { name: "View task" })).toBeEnabled(); expect(screen.getByRole("button", { name: "Return to sessions" })).toBeEnabled(); @@ -784,7 +796,7 @@ describe("PlanningModeModal sequential flow", () => { // tasks={[]} proves the banner resolves the just-created Task object, not the tasks prop. render(); - fireEvent.click(await screen.findByRole("button", { name: "Proceed with plan" })); + await clickProceedAfterHydration(); expect(await screen.findByTestId("planning-task-created")).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Continue planning" })); @@ -821,7 +833,7 @@ describe("PlanningModeModal sequential flow", () => { render(); - fireEvent.click(await screen.findByRole("button", { name: "Proceed with plan" })); + await clickProceedAfterHydration(); expect(await screen.findByTestId("planning-create-retry")).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Retry create" })); diff --git a/packages/dashboard/vitest.config.ts b/packages/dashboard/vitest.config.ts index 7fbb3e201c..4136cce662 100644 --- a/packages/dashboard/vitest.config.ts +++ b/packages/dashboard/vitest.config.ts @@ -350,11 +350,6 @@ uses pi-ai's real bundled catalog behind a no-refresh registry seam, after live roughly 300-second stall. Its paired ledger entry is removed in the same commit; no timeout or retry changed. */ const quarantinedDashboardTests: string[] = [ - /* - FNXC:DashboardTestQuarantine 2026-08-06-15:08: - FN-8816 observed PlanningModeModal's direct task-handoff test fail a second time only under a loaded planning-flow file, after its first-sighting register entry. Quarantine the entire file with its ledger row rather than weaken user-flow assertions or add timing retries; delete it at the ratchet deadline unless a root-cause rescue restores stable coverage. - */ - "app/components/__tests__/PlanningModeModal.planning-flow.test.tsx", /* FNXC:DashboardTestQuarantine 2026-07-17-16:50: FN-8245 re-admits all three UI files with their ledger rows removed in lockstep. diff --git a/scripts/lib/test-quarantine.json b/scripts/lib/test-quarantine.json index 830c3be284..c00b945342 100644 --- a/scripts/lib/test-quarantine.json +++ b/scripts/lib/test-quarantine.json @@ -5,11 +5,6 @@ "file": "packages/engine/src/__tests__/project-engine.test.ts", "reason": "FN-8811 observed B4/B5 workspace busy contention asserting a stale 60s cap while runtime schedules 120s, then the subprocess guard timed out on git remote; reproduced in isolation on 2026-08-06. Do not widen timing or weaken the assertion; restore only with a root-cause fix before the deletion deadline.", "quarantinedAt": "2026-08-06" - }, - { - "file": "packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx", - "reason": "FN-8816 observed the previously registered direct task-handoff failure a second time in a loaded planning-flow run while the exact test passed in isolation. Quarantined under the deletion ratchet rather than weakening assertions or adding retries; delete by 2026-08-20 unless a root-cause rescue restores stable coverage.", - "quarantinedAt": "2026-08-06" } ] }