diff --git a/.changeset/FN-7653-plan-auto-approve-intake-only.md b/.changeset/FN-7653-plan-auto-approve-intake-only.md new file mode 100644 index 0000000000..7b346746ce --- /dev/null +++ b/.changeset/FN-7653-plan-auto-approve-intake-only.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Auto-approve plan toggle now appears only on the planning column, not Todo. +category: fix +dev: Board.tsx gated the plan auto-approve prop pair on intake||hold; the built-in Coding workflow's Todo is a hold column, leaking the control. Gate is now intake-only (legacy triage path unchanged). diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index 7046357541..f64257854e 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -219,8 +219,9 @@ FNXC:TaskCardMobileSelection 2026-07-01-00:00: Mobile Board long-press is a task - -- The Triage column actions dropdown includes **Auto-approve plan**. Turning it on sets the project plan approval mode to auto-approve all planned tasks; turning it off returns to the workflow/default plan approval behavior. In workflow-mode Boards, the same switch appears on intake/hold planning columns and on the equivalent **All workflows** aggregate intake column. Use Settings → Merge for the full three-state project control, including **Require approval for all tasks**. + +- The Triage/Planning column actions dropdown includes **Auto-approve plan**. Turning it on sets the project plan approval mode to auto-approve all planned tasks; turning it off returns to the workflow/default plan approval behavior. In workflow-mode Boards, the same switch appears only on the intake/planning column and on the equivalent **All workflows** aggregate intake column — not on hold (Todo-like) or other lifecycle columns. Use Settings → Merge for the full three-state project control, including **Require approval for all tasks**. - Column ordering semantics: `todo` mirrors scheduler pickup order (priority descending, then oldest `createdAt`, then task ID); `triage`, `in-progress`, `in-review`, and `archived` remain priority-first with task-ID tie-breaks; `done` defaults to most recent completion first (`columnMovedAt`, then `updatedAt`, then `createdAt` fallback) and can be switched from the Done/complete column actions dropdown to descending task ID. In workflow mode, non-archived columns marked with the `complete` flag use the same Done menu items even when their column ID or label is customized. - Done-column sorting has two descending modes: **Completion date (newest first)** keeps the default completion-time order, while **Task ID (newest first)** places the highest numeric task IDs first. The sort actions are only shown in Done/complete column action menus, including custom workflow completion lanes; Archive All Done lives in the same menu when available. - On mobile, both default and workflow-mode boards fill the project viewport while the column strip remains the internal horizontal scroller with contained edge overscroll. diff --git a/packages/dashboard/app/components/Board.tsx b/packages/dashboard/app/components/Board.tsx index 04d6b5934b..854ac072bc 100644 --- a/packages/dashboard/app/components/Board.tsx +++ b/packages/dashboard/app/components/Board.tsx @@ -906,7 +906,8 @@ export function Board({ tasks, projectId, maxConcurrent, showWorktreeGrouping, o prAuthAvailable={prAuthAvailable} autoMerge={autoMerge} mergeStrategy={mergeStrategy} - {...(((columnDef.flags.intake || columnDef.flags.hold) && !columnDef.flags.archived && !columnDef.flags.complete && !columnDef.flags.countsTowardWip && !columnDef.flags.mergeBlocker && !columnDef.flags.humanReview) ? { planAutoApproveEnabled, onTogglePlanAutoApprove } : {})} + // FNXC:PlanApproval 2026-07-07-00:00: FN-7653 — the plan auto-approve shortcut belongs only to the intake/planning column, never to hold (Todo-like) columns; the built-in Coding workflow's Todo column carries the hold trait and was wrongly receiving this prop pair. + {...((columnDef.flags.intake && !columnDef.flags.archived && !columnDef.flags.complete && !columnDef.flags.countsTowardWip && !columnDef.flags.mergeBlocker && !columnDef.flags.humanReview) ? { planAutoApproveEnabled, onTogglePlanAutoApprove } : {})} {...(isCreateColumn && aggregateQuickCreateTarget ? { workflowId: aggregateQuickCreateTarget.workflowId, workflowOptions, defaultWorkflowId: boardWorkflows?.defaultWorkflowId ?? null, onQuickCreate: handleAggregateWorkflowQuickCreate, onNewTask, onPlanningMode, onSubtaskBreakdown } : {})} {...(columnDef.flags.mergeBlocker || columnDef.flags.humanReview ? { onToggleAutoMerge: handleToggleAutoMerge } : {})} {...(columnDef.id === "done" ? { onArchiveAllDone } : {})} @@ -987,7 +988,8 @@ export function Board({ tasks, projectId, maxConcurrent, showWorktreeGrouping, o prAuthAvailable={prAuthAvailable} autoMerge={autoMerge} mergeStrategy={mergeStrategy} - {...(((columnDef.flags.intake || columnDef.flags.hold) && !columnDef.flags.archived && !columnDef.flags.complete && !columnDef.flags.countsTowardWip && !columnDef.flags.mergeBlocker && !columnDef.flags.humanReview) ? { planAutoApproveEnabled, onTogglePlanAutoApprove } : {})} + // FNXC:PlanApproval 2026-07-07-00:00: FN-7653 — the plan auto-approve shortcut belongs only to the intake/planning column, never to hold (Todo-like) columns; the built-in Coding workflow's Todo column carries the hold trait and was wrongly receiving this prop pair. + {...((columnDef.flags.intake && !columnDef.flags.archived && !columnDef.flags.complete && !columnDef.flags.countsTowardWip && !columnDef.flags.mergeBlocker && !columnDef.flags.humanReview) ? { planAutoApproveEnabled, onTogglePlanAutoApprove } : {})} {...(isCreateColumn ? { workflowOptions, defaultWorkflowId: selectedWorkflow.id, onQuickCreate: handleWorkflowQuickCreate, onNewTask, onPlanningMode, onSubtaskBreakdown } : {})} {...(columnDef.flags.mergeBlocker || columnDef.flags.humanReview ? { onToggleAutoMerge: handleToggleAutoMerge } : {})} {...(columnDef.id === "done" ? { onArchiveAllDone } : {})} diff --git a/packages/dashboard/app/components/Column.tsx b/packages/dashboard/app/components/Column.tsx index 1adfe1ee24..d7cf63a512 100644 --- a/packages/dashboard/app/components/Column.tsx +++ b/packages/dashboard/app/components/Column.tsx @@ -577,7 +577,10 @@ function ColumnComponent({ column, tasks, projectId, maxConcurrent, showWorktree const showDoneArchiveAction = isDoneSortColumn && !!onArchiveAllDone; /* FNXC:PlanApproval 2026-07-01-08:44: - Triage and workflow intake/hold column actions need a Board shortcut that mirrors the project auto-approve plan override without replacing Settings modal's full workflow/auto-approve/require-all editor. + Triage and workflow intake/planning column actions need a Board shortcut that mirrors the project auto-approve plan override without replacing Settings modal's full workflow/auto-approve/require-all editor. + + FNXC:PlanApproval 2026-07-07-00:00 (FN-7653 correction): + This shortcut belongs ONLY to the intake/planning column, never to hold (Todo-like) columns — the built-in Coding workflow's Todo column carries the hold trait and was wrongly surfacing this toggle. Board.tsx is the single source of truth gating which columns receive `onTogglePlanAutoApprove`; Column.tsx just renders whatever prop it is given, so the fix lives in Board.tsx's intake-only gate, not here. */ const hasPlanAutoApproveAction = !!onTogglePlanAutoApprove; const hasDoneMenuActions = showDoneSortControl || showDoneArchiveAction; diff --git a/packages/dashboard/app/components/__tests__/Board.test.tsx b/packages/dashboard/app/components/__tests__/Board.test.tsx index 80096fca84..763b6b0b4e 100644 --- a/packages/dashboard/app/components/__tests__/Board.test.tsx +++ b/packages/dashboard/app/components/__tests__/Board.test.tsx @@ -1259,7 +1259,9 @@ describe("Board", () => { } }); - it("passes plan auto-approval toggle to selected workflow intake and hold columns only", async () => { + it("passes plan auto-approval toggle to the selected workflow intake column only, not hold", async () => { + // FNXC:PlanApproval 2026-07-07-00:00 (FN-7653): the hold column must NOT receive the toggle; + // only the intake/planning column does. Regression coverage for the Todo-shows-toggle bug. const workflow = { id: "wf-plan-columns", name: "Plan columns", @@ -1276,13 +1278,24 @@ describe("Board", () => { await waitFor(() => expect(screen.getByTestId("column-idea")).toBeDefined()); expect(screen.getByTestId("column-idea").getAttribute("data-has-plan-auto-approve-toggle")).toBe("yes"); - expect(screen.getByTestId("column-hold").getAttribute("data-has-plan-auto-approve-toggle")).toBe("yes"); + expect(screen.getByTestId("column-hold").getAttribute("data-has-plan-auto-approve-toggle")).toBe("no"); expect(screen.getByTestId("column-work").getAttribute("data-has-plan-auto-approve-toggle")).toBe("no"); expect(screen.getByTestId("column-review").getAttribute("data-has-plan-auto-approve-toggle")).toBe("no"); expect(screen.getByTestId("column-done").getAttribute("data-has-plan-auto-approve-toggle")).toBe("no"); }); - it("passes plan auto-approval toggle to all-workflows aggregate intake columns", async () => { + it("reproduces then disproves the Todo/hold column plan-toggle symptom on the built-in Coding workflow", async () => { + // FNXC:PlanApproval 2026-07-07-00:00 (FN-7653): exact reported symptom — built-in Coding workflow's + // `todo` column carries the hold trait and used to wrongly render the Auto-approve plan toggle. + enableFlag({ "FN-1": "builtin:coding" }, [DEFAULT_WORKFLOW]); + renderBoard({ tasks: [mkTask({ id: "FN-1", column: "triage" })], planAutoApproveEnabled: true }); + + await waitFor(() => expect(screen.getByTestId("column-triage")).toBeDefined()); + expect(screen.getByTestId("column-triage").getAttribute("data-has-plan-auto-approve-toggle")).toBe("yes"); + expect(screen.getByTestId("column-todo").getAttribute("data-has-plan-auto-approve-toggle")).toBe("no"); + }); + + it("passes plan auto-approval toggle to all-workflows aggregate intake columns only, not hold", async () => { const projectId = "project-all-plan-columns"; enableFlag({ "FN-1": "builtin:coding" }, [DEFAULT_WORKFLOW]); window.localStorage.setItem(scopedKey(BOARD_WORKFLOW_SELECTION_STORAGE_KEY, projectId), ALL_WORKFLOWS_BOARD_VIEW_ID); @@ -1293,6 +1306,7 @@ describe("Board", () => { await waitFor(() => expect(screen.getByTestId("column-triage")).toBeDefined()); expect(screen.getByTestId("column-triage").getAttribute("data-has-plan-auto-approve-toggle")).toBe("yes"); + expect(screen.getByTestId("column-todo").getAttribute("data-has-plan-auto-approve-toggle")).toBe("no"); expect(screen.getByTestId("column-in-progress").getAttribute("data-has-plan-auto-approve-toggle")).toBe("no"); });