diff --git a/packages/dashboard/app/__tests__/api-git.test.ts b/packages/dashboard/app/__tests__/api-git.test.ts index 5db19d0e80..9ff94f6e75 100644 --- a/packages/dashboard/app/__tests__/api-git.test.ts +++ b/packages/dashboard/app/__tests__/api-git.test.ts @@ -1081,7 +1081,15 @@ describe("Planning Mode API", () => { createdAt: "2026-01-01T00:00:00.000Z", updatedAt: "2026-01-01T00:00:00.000Z", }; - globalThis.fetch = vi.fn().mockReturnValue(mockFetchResponse(true, createdTask, 201)); + /* + FNXC:PlanningMode 2026-07-23-23:35: + FN-8442 (36b318096) changed POST /planning/create-task to respond with a + { task, alreadyCreated } envelope (idempotent replay detection); the client + unwraps response.task. Mock the real server envelope, not a bare Task. + */ + globalThis.fetch = vi.fn().mockReturnValue( + mockFetchResponse(true, { task: createdTask, alreadyCreated: false }, 201) + ); const result = await createTaskFromPlanning("plan-123"); diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.tab-persistence.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.tab-persistence.test.tsx index af4d9f6605..791ae9aae8 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.tab-persistence.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.tab-persistence.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"; import { describe, expect, it } from "vitest"; import type { Column, Task, TaskDetail } from "@fusion/core"; import { @@ -232,8 +232,16 @@ describe("TaskDetailModal tab persistence", () => { const view = renderDetail({ task }); await waitFor(() => expect(screen.getByRole("button", { name: "Session" })).toBeInTheDocument()); + /* + FNXC:TaskDetailTabs 2026-07-24-00:00: + Settle pending CLI-session fetch commits and click a freshly-queried node: clicking + right after the tab appears raced the cliSession hydration re-render on loaded CI + shards (full-suite run 30070825088), dispatching on a detached node so the tab never + activated. Same detached-node class d7752931b fixed for PlanningModeModal Proceed. + */ + await act(async () => {}); fireEvent.click(screen.getByRole("button", { name: "Session" })); - expect(screen.getByRole("button", { name: "Session" })).toHaveClass("detail-tab-active"); + await waitFor(() => expect(screen.getByRole("button", { name: "Session" })).toHaveClass("detail-tab-active")); rerenderDetail(view.rerender, { task: makeTask({ id: "FN-NO-SESSION", column: "in-progress", prompt: "# Full task" }) }); await waitFor(() => expect(screen.getByRole("button", { name: "Plan" })).toHaveClass("detail-tab-active")); diff --git a/packages/dashboard/src/__tests__/routes-auth.test.ts b/packages/dashboard/src/__tests__/routes-auth.test.ts index f350706214..02e699ab5d 100644 --- a/packages/dashboard/src/__tests__/routes-auth.test.ts +++ b/packages/dashboard/src/__tests__/routes-auth.test.ts @@ -237,6 +237,16 @@ function createMockStore(overrides: Partial = {}): TaskStore { addPrInfo: vi.fn().mockResolvedValue(undefined), updateIssueInfo: vi.fn().mockResolvedValue(undefined), getRootDir: vi.fn().mockReturnValue("/fake/root"), + /* + FNXC:PluginMcpServers 2026-07-23-23:30: + FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin + MCP provider on every getProjectContext call. A store that already exposes + getProjectScopedPluginMcpServers is treated as runtime-owned and skips the + binder (which would otherwise call getPluginStore()); declare it here so the + route contracts under test stay isolated from plugin-loader bootstrapping. + Same alignment as remote-access-routes.test.ts (d7752931b). + */ + getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]), listWorkflowSteps: vi.fn().mockResolvedValue([]), createWorkflowStep: vi.fn(), getWorkflowStep: vi.fn(), diff --git a/packages/dashboard/src/__tests__/routes-automation.test.ts b/packages/dashboard/src/__tests__/routes-automation.test.ts index 6a0c1b0d67..093cf86032 100644 --- a/packages/dashboard/src/__tests__/routes-automation.test.ts +++ b/packages/dashboard/src/__tests__/routes-automation.test.ts @@ -231,6 +231,16 @@ function createMockStore(overrides: Partial = {}): TaskStore { updateIssueInfo: vi.fn().mockResolvedValue(undefined), getRootDir: vi.fn().mockReturnValue("/fake/root"), /* + FNXC:PluginMcpServers 2026-07-23-23:40: + FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin + MCP provider on every getProjectContext call. A store that already exposes + getProjectScopedPluginMcpServers is treated as runtime-owned and skips the + binder (which would otherwise call getPluginStore()); declare it here so the + route contracts under test stay isolated from plugin-loader bootstrapping. + Same alignment as remote-access-routes.test.ts (d7752931b). + */ + getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]), + /* FNXC:DashboardTests 2026-07-18-09:45: Backup automation path calls resolveGlobalBackupRoot(store) which requires getGlobalSettingsDir. Full-suite shard 4 failed FN-7537 manual backup parity diff --git a/packages/dashboard/src/__tests__/routes-git.test.ts b/packages/dashboard/src/__tests__/routes-git.test.ts index c523d848d9..c528df5593 100644 --- a/packages/dashboard/src/__tests__/routes-git.test.ts +++ b/packages/dashboard/src/__tests__/routes-git.test.ts @@ -207,6 +207,16 @@ function createMockStore(overrides: Partial = {}): TaskStore { updatePrInfo: vi.fn().mockResolvedValue(undefined), updateIssueInfo: vi.fn().mockResolvedValue(undefined), getRootDir: vi.fn().mockReturnValue("/fake/root"), + /* + FNXC:PluginMcpServers 2026-07-23-23:40: + FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin + MCP provider on every getProjectContext call. A store that already exposes + getProjectScopedPluginMcpServers is treated as runtime-owned and skips the + binder (which would otherwise call getPluginStore()); declare it here so the + route contracts under test stay isolated from plugin-loader bootstrapping. + Same alignment as remote-access-routes.test.ts (d7752931b). + */ + getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]), listWorkflowSteps: vi.fn().mockResolvedValue([]), createWorkflowStep: vi.fn(), getWorkflowStep: vi.fn(), diff --git a/packages/dashboard/src/__tests__/routes-github.test.ts b/packages/dashboard/src/__tests__/routes-github.test.ts index 27e8d124e7..baf081f2c6 100644 --- a/packages/dashboard/src/__tests__/routes-github.test.ts +++ b/packages/dashboard/src/__tests__/routes-github.test.ts @@ -251,6 +251,16 @@ function createMockStore(overrides: Partial = {}): TaskStore { removePrInfoByNumber: vi.fn().mockResolvedValue(undefined), updateIssueInfo: vi.fn().mockResolvedValue(undefined), getRootDir: vi.fn().mockReturnValue("/fake/root"), + /* + FNXC:PluginMcpServers 2026-07-23-23:40: + FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin + MCP provider on every getProjectContext call. A store that already exposes + getProjectScopedPluginMcpServers is treated as runtime-owned and skips the + binder (which would otherwise call getPluginStore()); declare it here so the + route contracts under test stay isolated from plugin-loader bootstrapping. + Same alignment as remote-access-routes.test.ts (d7752931b). + */ + getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]), listWorkflowSteps: vi.fn().mockResolvedValue([]), createWorkflowStep: vi.fn(), getWorkflowStep: vi.fn(), @@ -1994,6 +2004,17 @@ describe("projectId store scoping regressions", () => { keyDeliverables: [], }); vi.spyOn(planningModule, "cleanupSession").mockImplementation(() => {}); + /* + FNXC:PlanningMode 2026-07-23-23:55: + FN-8442 (36b318096) routes create-task through durable create-claim plumbing + (getDurablePlanningSession + updatePlanningCreateClaim). Those functions call the + REAL module-internal getSession (a namespace spy cannot intercept intra-module + calls), so the spied session above is invisible to them and they throw + SessionNotFoundError. This test's contract is scoped-store routing of createTask, + not claim persistence, so stub the claim plumbing to inert no-ops here. + */ + vi.spyOn(planningModule, "getDurablePlanningSession").mockResolvedValue(undefined); + vi.spyOn(planningModule, "updatePlanningCreateClaim").mockResolvedValue(undefined); (scopedStore.createTask as ReturnType).mockResolvedValue({ ...FAKE_TASK_DETAIL, diff --git a/packages/dashboard/src/__tests__/routes-tasks.test.ts b/packages/dashboard/src/__tests__/routes-tasks.test.ts index 558b508f47..f06efec103 100644 --- a/packages/dashboard/src/__tests__/routes-tasks.test.ts +++ b/packages/dashboard/src/__tests__/routes-tasks.test.ts @@ -236,6 +236,16 @@ function createMockStore(overrides: Partial = {}): TaskStore { recordActivity: vi.fn().mockResolvedValue(undefined), getFusionDir: vi.fn().mockReturnValue("/fake/root/.fusion"), getRootDir: vi.fn().mockReturnValue("/fake/root"), + /* + FNXC:PluginMcpServers 2026-07-23-23:40: + FN-8491 (3cd023fa4) made resolveProjectContext bind a project-scoped plugin + MCP provider on every getProjectContext call. A store that already exposes + getProjectScopedPluginMcpServers is treated as runtime-owned and skips the + binder (which would otherwise call getPluginStore()); declare it here so the + route contracts under test stay isolated from plugin-loader bootstrapping. + Same alignment as remote-access-routes.test.ts (d7752931b). + */ + getProjectScopedPluginMcpServers: vi.fn().mockResolvedValue([]), // FNXC:PostgresCutover 2026-07-05-16:20: routes borrow the AsyncDataLayer // from the scoped store (e.g. triggerCommentWakeForAssignedAgent builds a // backend AgentStore). null = legacy mode for this mock.