diff --git a/packages/dashboard/app/__tests__/settings-sections.test.tsx b/packages/dashboard/app/__tests__/settings-sections.test.tsx index 8433f3213d..dd141052f8 100644 --- a/packages/dashboard/app/__tests__/settings-sections.test.tsx +++ b/packages/dashboard/app/__tests__/settings-sections.test.tsx @@ -143,7 +143,13 @@ describe("GeneralSection", () => { await waitFor(() => expect(screen.getByLabelText("Coding")).toBeInTheDocument()); expect(screen.queryByLabelText("Brainstorming")).not.toBeInTheDocument(); - expect(screen.queryByLabelText("Coding (Ideas)")).not.toBeInTheDocument(); + /* + FNXC:SettingsWorkflowToggles 2026-07-23-22:05: + PR #2378 restored builtin:coding-ideas (removed from DEPRECATED_BUILTIN_WORKFLOW_IDS), + so Coding (Ideas) is a live selectable built-in again and must render a toggle. + Brainstorming remains the deprecated built-in that must stay hidden. + */ + expect(screen.getByLabelText("Coding (Ideas)")).toBeInTheDocument(); }); it("emits the absolute file-browser path toggle via setForm", () => { diff --git a/packages/dashboard/app/components/WorkflowNodeEditor.css b/packages/dashboard/app/components/WorkflowNodeEditor.css index 013f6493fe..b08cc28abd 100644 --- a/packages/dashboard/app/components/WorkflowNodeEditor.css +++ b/packages/dashboard/app/components/WorkflowNodeEditor.css @@ -2072,10 +2072,18 @@ Fusion tokens so light/dark themes never show browser-default white controls. } .wf-column-description-field { + /* + FNXC:DashboardTextTokens 2026-07-23-22:30: + FN-8526 shipped this label with the undefined deprecated --text-secondary alias; per the FN-8043 + migration guarded by deprecated-text-secondary-token-migration.test.ts, secondary text uses the + canonical --text-muted token, which resolves in both base and light themes. + (Comment lives inside the block so WorkflowColumnPanel.test.tsx's brace-scanning selector parser + still sees the bare selector.) + */ display: flex; flex-direction: column; gap: var(--space-xs); - color: var(--text-secondary); + color: var(--text-muted); font-size: 0.75rem; } diff --git a/packages/dashboard/app/components/__tests__/ChatView.chat-commands.test.tsx b/packages/dashboard/app/components/__tests__/ChatView.chat-commands.test.tsx index a13ec3d411..292a98ad1a 100644 --- a/packages/dashboard/app/components/__tests__/ChatView.chat-commands.test.tsx +++ b/packages/dashboard/app/components/__tests__/ChatView.chat-commands.test.tsx @@ -164,7 +164,17 @@ describe("ChatView slash-command dispatch (/steer)", () => { fireEvent.change(textarea, { target: { value: "hello there" } }); fireEvent.keyDown(textarea, { key: "Enter" }); - await waitFor(() => expect(sendMessage).toHaveBeenCalledWith("hello there", [])); + /* + FNXC:ChatAttachments 2026-07-23-22:35: + FN-8502 made ChatView pass attachment delivery callbacks as a third + sendMessage argument ({ onDelivered, onFailed }); assert them structurally + so the /steer dispatch contract (text + empty attachments, no steering + comment) stays the protected invariant. + */ + await waitFor(() => expect(sendMessage).toHaveBeenCalledWith("hello there", [], expect.objectContaining({ + onDelivered: expect.any(Function), + onFailed: expect.any(Function), + }))); expect(mockAddSteeringComment).not.toHaveBeenCalled(); }); @@ -181,7 +191,11 @@ describe("ChatView slash-command dispatch (/steer)", () => { fireEvent.change(textarea, { target: { value: "please /steer this" } }); fireEvent.keyDown(textarea, { key: "Enter" }); - await waitFor(() => expect(sendMessage).toHaveBeenCalledWith("please /steer this", [])); + // FNXC:ChatAttachments 2026-07-23-22:35: FN-8502 delivery-callback third arg (see above). + await waitFor(() => expect(sendMessage).toHaveBeenCalledWith("please /steer this", [], expect.objectContaining({ + onDelivered: expect.any(Function), + onFailed: expect.any(Function), + }))); expect(mockAddSteeringComment).not.toHaveBeenCalled(); }); diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.models-progress-workflow.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.models-progress-workflow.test.tsx index 14537d0478..8f8e20c290 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.models-progress-workflow.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.models-progress-workflow.test.tsx @@ -1065,7 +1065,8 @@ describe("TaskDetailModal", () => { vi.mocked(fetchBoardWorkflows).mockResolvedValueOnce({ flagEnabled: true, defaultWorkflowId: "wf-edit", - workflows: [{ id: "wf-edit", name: "Edit Workflow" }], + // FNXC:TaskDetailWorkflow 2026-07-23-22:10: FN-8476 (restore Ideas detail move action) made resolveTaskWorkflowMetadata derive move targets from workflow.columns, so mocked workflows must carry a columns array or metadata resolution throws and the edit-mode optional-steps picker never mounts. + workflows: [{ id: "wf-edit", name: "Edit Workflow", columns: [] }], taskWorkflowIds: { "FN-099": "wf-edit" }, } as any); vi.mocked(fetchWorkflowOptionalSteps).mockResolvedValueOnce([ @@ -1108,7 +1109,8 @@ describe("TaskDetailModal", () => { vi.mocked(fetchBoardWorkflows).mockResolvedValueOnce({ flagEnabled: true, defaultWorkflowId: "wf-edit", - workflows: [{ id: "wf-edit", name: "Edit Workflow" }], + // FNXC:TaskDetailWorkflow 2026-07-23-22:10: FN-8476 (restore Ideas detail move action) made resolveTaskWorkflowMetadata derive move targets from workflow.columns, so mocked workflows must carry a columns array or metadata resolution throws and the edit-mode optional-steps picker never mounts. + workflows: [{ id: "wf-edit", name: "Edit Workflow", columns: [] }], taskWorkflowIds: { "FN-099": "wf-edit" }, } as any); vi.mocked(fetchWorkflowOptionalSteps).mockResolvedValueOnce([ diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-controls.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-controls.test.tsx index 4b0082814a..7e3bed4f9c 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-controls.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-controls.test.tsx @@ -92,7 +92,7 @@ describe("TaskDetailModal oversight controls", () => { vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({ flagEnabled: true, defaultWorkflowId: "WF-advisor", - workflows: [{ id: "WF-advisor", name: "Advisor workflow" } as any], + workflows: [{ id: "WF-advisor", name: "Advisor workflow", columns: [] } as any], taskWorkflowIds: { "FN-8247-workflow": "WF-advisor" }, }); vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({ @@ -134,7 +134,7 @@ describe("TaskDetailModal oversight controls", () => { vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({ flagEnabled: true, defaultWorkflowId: "WF-8263-project-default", - workflows: [{ id: "WF-8263-project-default", name: "Project default workflow" } as any], + workflows: [{ id: "WF-8263-project-default", name: "Project default workflow", columns: [] } as any], taskWorkflowIds: { "FN-8263-project-default": "WF-8263-project-default" }, }); vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({ @@ -177,10 +177,18 @@ describe("TaskDetailModal oversight controls", () => { defaultPresetBySize: {}, sessionAdvisorEnabledByDefault: true, } as any); - vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({ + /* + FNXC:PlannerOversight 2026-07-23-22:20: + FN-8476 made the board-workflows lookup re-run whenever the task prop identity + changes (it derives move metadata from the payload), so the onTaskUpdated + rerender below refetches. A once-mock would leave the refetch on the + flagEnabled:false default and silently drop the workflow tier mid-test — + keep the payload persistent for every call in this test. + */ + vi.mocked(api.fetchBoardWorkflows).mockResolvedValue({ flagEnabled: true, defaultWorkflowId: "WF-8263-pending-advisor", - workflows: [{ id: "WF-8263-pending-advisor", name: "Pending advisor workflow" } as any], + workflows: [{ id: "WF-8263-pending-advisor", name: "Pending advisor workflow", columns: [] } as any], taskWorkflowIds: { [currentTask.id]: "WF-8263-pending-advisor" }, }); vi.mocked(api.fetchWorkflowSettingValues).mockImplementationOnce(() => new Promise(() => {})); @@ -238,10 +246,12 @@ describe("TaskDetailModal oversight controls", () => { defaultPresetBySize: {}, sessionAdvisorEnabledByDefault: false, } as any); - vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({ + // FNXC:PlannerOversight 2026-07-23-22:20: persistent mock — FN-8476 refetches + // board workflows on each task-prop identity change (see pending-advisor test). + vi.mocked(api.fetchBoardWorkflows).mockResolvedValue({ flagEnabled: true, defaultWorkflowId: "WF-advisor-explicit-off", - workflows: [{ id: "WF-advisor-explicit-off", name: "Advisor workflow" } as any], + workflows: [{ id: "WF-advisor-explicit-off", name: "Advisor workflow", columns: [] } as any], taskWorkflowIds: { [currentTask.id]: "WF-advisor-explicit-off" }, }); vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({ diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-mobile.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-mobile.test.tsx index c02377d4ea..06afa169e2 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-mobile.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.oversight-mobile.test.tsx @@ -83,7 +83,7 @@ describe("TaskDetailModal oversight controls — mobile overflow menu", () => { vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({ flagEnabled: true, defaultWorkflowId: "WF-8263-mobile-project-default", - workflows: [{ id: "WF-8263-mobile-project-default", name: "Mobile project default workflow" } as any], + workflows: [{ id: "WF-8263-mobile-project-default", name: "Mobile project default workflow", columns: [] } as any], taskWorkflowIds: { "FN-8263-mobile-project-default": "WF-8263-mobile-project-default" }, }); vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({ @@ -129,7 +129,7 @@ describe("TaskDetailModal oversight controls — mobile overflow menu", () => { vi.mocked(api.fetchBoardWorkflows).mockResolvedValueOnce({ flagEnabled: true, defaultWorkflowId: "WF-advisor-mobile", - workflows: [{ id: "WF-advisor-mobile", name: "Advisor workflow" } as any], + workflows: [{ id: "WF-advisor-mobile", name: "Advisor workflow", columns: [] } as any], taskWorkflowIds: { [currentTask.id]: "WF-advisor-mobile" }, }); vi.mocked(api.fetchWorkflowSettingValues).mockResolvedValueOnce({ @@ -235,7 +235,7 @@ describe("TaskDetailModal oversight controls — mobile overflow menu", () => { vi.mocked(api.fetchBoardWorkflows).mockResolvedValue({ flagEnabled: true, defaultWorkflowId: "WF-mobile-test", - workflows: [{ id: "WF-mobile-test", name: "Mobile Test Workflow" } as any], + workflows: [{ id: "WF-mobile-test", name: "Mobile Test Workflow", columns: [] } as any], taskWorkflowIds: { "FN-212": "WF-mobile-test" }, }); vi.mocked(api.fetchWorkflowSettingValues).mockImplementation(() => new Promise(() => {})); diff --git a/packages/dashboard/app/components/settings/sections/GeneralSection.search.ts b/packages/dashboard/app/components/settings/sections/GeneralSection.search.ts index 13471e8cae..832052d295 100644 --- a/packages/dashboard/app/components/settings/sections/GeneralSection.search.ts +++ b/packages/dashboard/app/components/settings/sections/GeneralSection.search.ts @@ -8,6 +8,21 @@ import type { SettingsSearchEntry } from "../search/types"; export const generalSearchEntries: SettingsSearchEntry[] = [ + { + /* + FNXC:SettingsSearch 2026-07-23-22:20: + FN-8348 added the Report row (bug/feedback/idea/help entry point) to General + without indexing it, so settings search could not find it; the search-index + drift guard caught the gap. Labels/help mirror the section's t() copy verbatim. + */ + sectionId: "general", + key: "report-action-menu", + labelKey: "settings.general.report", + labelFallback: "Report", + helpKey: "settings.general.reportHelp", + helpFallback: "Report a bug, send feedback, share an idea, or get help from Fusion.", + keywords: ["bug", "feedback", "idea", "help", "support"], + }, { sectionId: "general", key: "taskPrefix",