diff --git a/.changeset/fn-7591-coding-ideas-intake.md b/.changeset/fn-7591-coding-ideas-intake.md
new file mode 100644
index 0000000000..6db08f8a77
--- /dev/null
+++ b/.changeset/fn-7591-coding-ideas-intake.md
@@ -0,0 +1,7 @@
+---
+"@runfusion/fusion": patch
+---
+
+summary: New tasks created under the Coding (Ideas) workflow now land in the Ideas column and wait for you to promote them.
+category: fix
+dev: Dashboard create surfaces (InlineCreateCard, QuickEntryBox, NewTaskModal, insight/todo → task) no longer hard-code column:"triage"; the store now resolves the selected/default workflow's intake column. InlineCreateCard forwards workflowId at create time instead of applying it post-create. Also fixed a glue-layer regression in `useTaskHandlers.ts` (`handleBoardQuickCreate`/`handleModalCreate`) that re-forced column:"triage" even after the UI surfaces stopped sending it.
diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md
index 8992295765..40cc1440e8 100644
--- a/docs/dashboard-guide.md
+++ b/docs/dashboard-guide.md
@@ -432,6 +432,10 @@ When quick-create task creation, Planning Mode, or Subtask Breakdown runs from a
The **New Task** dialog's workflow selector also defaults to the current or last selected Board/List workflow lane for the current project. If no valid lane has been selected, or the remembered lane was deleted, the selector falls back to the project default workflow and task creation omits an explicit `workflowId`.
+
+
+Create requests never send an explicit `column`. The task store resolves the landing column from the (selected or project-default) workflow's intake column, so most tasks still land in `triage` under the default Coding workflow, byte-identical to before. A workflow with a **manual intake column** — for example the built-in **Coding (Ideas)** workflow's `ideas` column (`autoTriage: false`) — parks new cards there instead: they wait for you to promote them into `todo` and are not auto-planned by the triage service until you do.
+
Optional workflow steps declared by the active workflow are available from the quick-add action row and the **New Task** dialog's inline quick buttons. For example, the coding workflow's browser verification option appears as a quick drop-down when that workflow is active; each option is seeded from the workflow step's `defaultOn` setting and is sent with the task's `enabledWorkflowSteps` payload at creation time.
diff --git a/docs/workflow-steps.md b/docs/workflow-steps.md
index 9e453b6d98..48db90a417 100644
--- a/docs/workflow-steps.md
+++ b/docs/workflow-steps.md
@@ -82,6 +82,7 @@ Use this inventory as the documentation map for current workflow behavior:
| Routing boundary | Agents may select/change a workflow only for explicit user requests or tasks they created; no-commit markers do not imply Quick fix or any other workflow. | This page, [Selecting workflows](#selecting-workflows); [Agents](./agents.md#interactive-cli-chat). |
| Dashboard board/list/graph selection | Board/List/Header/Graph share durable per-project workflow selection; stale saved ids fall back to a valid workflow. Board adds a dashboard-only **All workflows** aggregate and task workflow-name badges; Graph uses **All workflows** for the full active graph. | [Dashboard Guide → Board View](./dashboard-guide.md#board-view), [Graph View](./dashboard-guide.md#graph-view), and [Workflow Selection and Editor](./dashboard-guide.md#workflow-selection-and-editor). |
| Create/planning forwarding | Quick-create task creation, Planning Mode, Subtask Breakdown, and the New Task dialog forward the active real workflow id when creating tasks; **All workflows** quick-create chooses a real workflow intake/default column instead of saving a synthetic aggregate id. | [Dashboard Guide → Planning Mode](./dashboard-guide.md#planning-mode). |
+| Manual-intake column parking | Dashboard create surfaces never send an explicit `column`; the store resolves the landing column from the (selected or project-default) workflow's intake column. A workflow whose intake column sets `autoTriage: false` (e.g. built-in Coding (Ideas)'s `ideas` column) parks new cards there instead of auto-planning them, until an operator promotes the card. | [Dashboard Guide → Create/Planning Forwarding](./dashboard-guide.md#planning-mode). |
### Skill-backed workflow steps
diff --git a/packages/core/src/__tests__/store-create-intake-column.test.ts b/packages/core/src/__tests__/store-create-intake-column.test.ts
index 02b9a14452..f2c94ea21a 100644
--- a/packages/core/src/__tests__/store-create-intake-column.test.ts
+++ b/packages/core/src/__tests__/store-create-intake-column.test.ts
@@ -36,6 +36,26 @@ describe("createTask intake-column wiring (Coding (Ideas))", () => {
expect(task.column).toBe("ideas");
});
+ it("lands a task explicitly selecting builtin:coding in triage even when the project default is coding-ideas", async () => {
+ const store = harness.store();
+ await store.setDefaultWorkflowId("builtin:coding-ideas");
+ const task = await store.createTask({
+ description: "explicit default coding workflow task",
+ workflowId: "builtin:coding",
+ });
+ expect(task.column).toBe("triage");
+ });
+
+ it("does not throw and falls back to triage when workflowId is explicitly null (\"No workflow\")", async () => {
+ const store = harness.store();
+ await store.setDefaultWorkflowId("builtin:coding-ideas");
+ const task = await store.createTask({
+ description: "explicit no-workflow task",
+ workflowId: null,
+ });
+ expect(task.column).toBe("triage");
+ });
+
it("writes a bootstrap PROMPT.md for an ideas-column task (unplanned)", async () => {
const store = harness.store();
const task: Task = await store.createTask({
diff --git a/packages/dashboard/app/App.tsx b/packages/dashboard/app/App.tsx
index 03532054fe..8264e175c1 100644
--- a/packages/dashboard/app/App.tsx
+++ b/packages/dashboard/app/App.tsx
@@ -792,10 +792,13 @@ function AppInner() {
const handleInsightTaskCreate = useCallback(
async ({ insightId, title, description }: { insightId: string; title: string; description: string }) => {
+ /*
+ FNXC:CodingIdeasWorkflow 2026-07-05-00:00:
+ Do not hard-code `column: "triage"` — this surface has no workflow picker, so it inherits the project-default workflow, and the store resolves the landing column from that workflow's intake column (e.g. Coding (Ideas) → "ideas") instead of forcing triage.
+ */
await createTask({
title,
description,
- column: "triage",
source: {
sourceType: "dashboard_ui",
sourceMetadata: {
diff --git a/packages/dashboard/app/components/InlineCreateCard.tsx b/packages/dashboard/app/components/InlineCreateCard.tsx
index ebf302edba..134559d580 100644
--- a/packages/dashboard/app/components/InlineCreateCard.tsx
+++ b/packages/dashboard/app/components/InlineCreateCard.tsx
@@ -6,7 +6,7 @@ import { Brain, Link, ListTree, Zap, ChevronDown, ChevronUp, Bot, Maximize2, Min
import { DEFAULT_TASK_PRIORITY, TASK_PRIORITIES, type Task, type TaskPriority, type Settings, type ResolvedWorkflowOptionalStep } from "@fusion/core";
import { getErrorMessage } from "@fusion/core";
import type { ToastType } from "../hooks/useToast";
-import { checkDuplicateTasks, fetchModels, uploadAttachment, fetchSettings, updateGlobalSettings, fetchAgents, selectTaskWorkflow, fetchWorkflowOptionalSteps, DuplicateCandidatesError } from "../api";
+import { checkDuplicateTasks, fetchModels, uploadAttachment, fetchSettings, updateGlobalSettings, fetchAgents, fetchWorkflowOptionalSteps, DuplicateCandidatesError } from "../api";
import type { CreateTaskInput, ModelInfo, Agent, NodeInfo, DuplicateMatch } from "../api";
import { useNodes } from "../hooks/useNodes";
import { ModelSelectionModal } from "./ModelSelectionModal";
@@ -394,24 +394,15 @@ export function InlineCreateCard({
});
}, []);
+ /*
+ FNXC:CodingIdeasWorkflow 2026-07-05-00:00:
+ submitTask no longer applies the selected workflow post-create via selectTaskWorkflow — handleSubmit now forwards workflowId inside the CreateTaskInput so the store materializes the workflow and resolves the intake column (e.g. Coding (Ideas) → "ideas") atomically at create time. A post-create selectTaskWorkflow call would race the store's intake-column resolution and re-introduce the auto-triage bug this task fixes.
+ */
const submitTask = useCallback(async (input: CreateTaskInput) => {
setSubmitting(true);
try {
const task = await onSubmit(input);
- // Apply custom workflow if selected (non-blocking — task already exists)
- if (selectedWorkflowId) {
- try {
- await selectTaskWorkflow(task.id, selectedWorkflowId, projectId);
- } catch (err) {
- if (addToast) {
- addToast(getErrorMessage(err) || "Failed to apply workflow", "error");
- } else {
- console.warn("Failed to apply workflow:", getErrorMessage(err));
- }
- }
- }
-
// Upload pending images as attachments
if (pendingImages.length > 0) {
const failures: string[] = [];
@@ -478,15 +469,18 @@ export function InlineCreateCard({
onSubmit,
addToast,
projectId,
- selectedWorkflowId,
]);
const handleSubmit = useCallback(async () => {
if (!description.trim() || submitting) return;
+ /*
+ FNXC:CodingIdeasWorkflow 2026-07-05-00:00:
+ Do not hard-code `column: "triage"` here — the store resolves the landing column from the forwarded (or project-default) workflow's intake column, e.g. Coding (Ideas) → "ideas". Forwarding `workflowId` at create time (instead of applying it post-create via selectTaskWorkflow) lets the store materialize the workflow and land the card in its resolved intake column atomically, so a manual-intake workflow parks the card for the operator instead of being auto-triaged.
+ */
const input: CreateTaskInput = {
description: description.trim(),
- column: "triage",
+ ...(selectedWorkflowId ? { workflowId: selectedWorkflowId } : {}),
dependencies: dependencies.length ? dependencies : undefined,
...(selectedAgentId ? { assignedAgentId: selectedAgentId } : {}),
modelPresetId: selectedPresetId,
@@ -517,7 +511,7 @@ export function InlineCreateCard({
}
await submitTask(input);
- }, [description, submitting, dependencies, selectedAgentId, selectedPresetId, hasExecutorOverride, executorProvider, executorModelId, hasValidatorOverride, validatorProvider, validatorModelId, hasPlanningOverride, planningProvider, planningModelId, optionalSteps.length, enabledOptionalStepIds, priority, effectiveNodeId, projectId, addToast, submitTask]);
+ }, [description, submitting, selectedWorkflowId, dependencies, selectedAgentId, selectedPresetId, hasExecutorOverride, executorProvider, executorModelId, hasValidatorOverride, validatorProvider, validatorModelId, hasPlanningOverride, planningProvider, planningModelId, optionalSteps.length, enabledOptionalStepIds, priority, effectiveNodeId, projectId, addToast, submitTask]);
const handleDuplicateProceed = useCallback(async () => {
const matches = duplicateMatches;
diff --git a/packages/dashboard/app/components/NewTaskModal.tsx b/packages/dashboard/app/components/NewTaskModal.tsx
index ab0251100f..2cca65baf3 100644
--- a/packages/dashboard/app/components/NewTaskModal.tsx
+++ b/packages/dashboard/app/components/NewTaskModal.tsx
@@ -791,10 +791,13 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
const validatorSlashIdx = validatorModel.indexOf("/");
const planningSlashIdx = planningModel.indexOf("/");
+ /*
+ FNXC:CodingIdeasWorkflow 2026-07-05-00:00:
+ Do not hard-code `column: "triage"` — the store resolves the landing column from the (materialized) workflowId below, so a manual-intake workflow (e.g. Coding (Ideas) → "ideas") parks the card for the operator instead of being auto-triaged.
+ */
const createInput: NewTaskCreateInput = {
title: undefined,
description: trimmedDesc,
- column: "triage",
dependencies: dependencies.length ? dependencies : undefined,
// U6/R3: forward the workflow selection only when the user changed it.
// - undefined → omit (store inherits the project default, today's behavior)
diff --git a/packages/dashboard/app/components/QuickEntryBox.tsx b/packages/dashboard/app/components/QuickEntryBox.tsx
index ad8745a4e5..87ca77fb7d 100644
--- a/packages/dashboard/app/components/QuickEntryBox.tsx
+++ b/packages/dashboard/app/components/QuickEntryBox.tsx
@@ -715,9 +715,12 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
const originalDescription = description;
setDescription("");
try {
+ /*
+ FNXC:CodingIdeasWorkflow 2026-07-05-00:00:
+ Do not hard-code `column: "triage"` — the store resolves the landing column from the forwarded (or project-default) workflow's intake column, so a manual-intake workflow (e.g. Coding (Ideas) → "ideas") parks the card for the operator instead of being auto-triaged.
+ */
const createdTask = await onCreate({
description: trimmed,
- column: "triage",
...(selectedWorkflowForCreate !== undefined ? { workflowId: selectedWorkflowForCreate } : {}),
dependencies: dependencies.length ? dependencies : undefined,
...(selectedAgentId ? { assignedAgentId: selectedAgentId } : {}),
diff --git a/packages/dashboard/app/components/TodoView.tsx b/packages/dashboard/app/components/TodoView.tsx
index 916962ee7c..c76e777438 100644
--- a/packages/dashboard/app/components/TodoView.tsx
+++ b/packages/dashboard/app/components/TodoView.tsx
@@ -299,9 +299,12 @@ export function TodoView({
const handleCreateTaskFromItem = useCallback(async (item: TodoItem) => {
try {
+ /*
+ FNXC:CodingIdeasWorkflow 2026-07-05-00:00:
+ Do not hard-code `column: "triage"` — this surface has no workflow picker, so it inherits the project-default workflow, and the store resolves the landing column from that workflow's intake column (e.g. Coding (Ideas) → "ideas") instead of forcing triage.
+ */
const input: TaskCreateInput = {
description: item.text,
- column: "triage",
source: { sourceType: "dashboard_ui" },
};
const task: Task = await createTask(input, projectId);
@@ -314,9 +317,9 @@ export function TodoView({
const handleCreateTaskAndAssign = useCallback(async (item: TodoItem, agentId: string) => {
try {
+ // FNXC:CodingIdeasWorkflow 2026-07-05-00:00: same rationale as handleCreateTaskFromItem above — omit column so the project-default workflow's intake column resolves it.
const input: TaskCreateInput = {
description: item.text,
- column: "triage",
assignedAgentId: agentId,
source: { sourceType: "dashboard_ui" },
};
diff --git a/packages/dashboard/app/components/__tests__/InlineCreateCard.test.tsx b/packages/dashboard/app/components/__tests__/InlineCreateCard.test.tsx
index f08f49d86a..c4c5d35c34 100644
--- a/packages/dashboard/app/components/__tests__/InlineCreateCard.test.tsx
+++ b/packages/dashboard/app/components/__tests__/InlineCreateCard.test.tsx
@@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react";
import { InlineCreateCard } from "../InlineCreateCard";
import type { Task, Column } from "@fusion/core";
-import { fetchModels, fetchSettings, fetchAgents, checkDuplicateTasks, fetchWorkflows, fetchWorkflowOptionalSteps } from "../../api";
+import { fetchModels, fetchSettings, fetchAgents, checkDuplicateTasks, fetchWorkflows, fetchWorkflowOptionalSteps, selectTaskWorkflow } from "../../api";
import { useNodes } from "../../hooks/useNodes";
import type { ModelInfo } from "../../api";
import { scopedKey } from "../../utils/projectStorage";
@@ -1773,4 +1773,61 @@ describe("InlineCreateCard node override", () => {
});
});
+/*
+FN-7591: InlineCreateCard must forward a selected workflow inside the create-time CreateTaskInput
+(so the store materializes it and resolves the workflow's intake column atomically) instead of hard-coding
+column:"triage" and applying the workflow post-create via selectTaskWorkflow.
+*/
+describe("InlineCreateCard workflow selection at create time (FN-7591)", () => {
+ beforeEach(() => {
+ vi.mocked(fetchWorkflows).mockResolvedValue([
+ { id: "wf-a", name: "Workflow A" },
+ { id: "builtin:coding-ideas", name: "Coding (Ideas)" },
+ ]);
+ });
+
+ it("submits workflowId in the create input and omits column:triage when a workflow is selected", async () => {
+ const { props } = renderCard();
+ expandCard();
+
+ fireEvent.change(screen.getByPlaceholderText("What needs to be done?"), { target: { value: "Idea for later" } });
+ const select = await screen.findByLabelText("Workflow") as HTMLSelectElement;
+ fireEvent.change(select, { target: { value: "builtin:coding-ideas" } });
+
+ fireEvent.click(screen.getByTestId("save-button"));
+
+ await waitFor(() => expect(props.onSubmit).toHaveBeenCalled());
+ const submitted = vi.mocked(props.onSubmit).mock.calls[0][0];
+ expect(submitted.workflowId).toBe("builtin:coding-ideas");
+ expect(submitted.column).toBeUndefined();
+ });
+
+ it("omits workflowId when no workflow is explicitly selected (inherits project default)", async () => {
+ const { props } = renderCard();
+ expandCard();
+
+ fireEvent.change(screen.getByPlaceholderText("What needs to be done?"), { target: { value: "Plain task" } });
+ fireEvent.click(screen.getByTestId("save-button"));
+
+ await waitFor(() => expect(props.onSubmit).toHaveBeenCalled());
+ const submitted = vi.mocked(props.onSubmit).mock.calls[0][0];
+ expect(submitted.workflowId).toBeUndefined();
+ expect(submitted.column).toBeUndefined();
+ });
+
+ it("does not call the redundant post-create selectTaskWorkflow for the create path", async () => {
+ const { props } = renderCard();
+ expandCard();
+
+ fireEvent.change(screen.getByPlaceholderText("What needs to be done?"), { target: { value: "Idea for later" } });
+ const select = await screen.findByLabelText("Workflow") as HTMLSelectElement;
+ fireEvent.change(select, { target: { value: "builtin:coding-ideas" } });
+
+ fireEvent.click(screen.getByTestId("save-button"));
+
+ await waitFor(() => expect(props.onSubmit).toHaveBeenCalled());
+ expect(selectTaskWorkflow).not.toHaveBeenCalled();
+ });
+});
+
});
diff --git a/packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx b/packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx
index cd5b40d954..d7f30b1f1a 100644
--- a/packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx
+++ b/packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx
@@ -1373,7 +1373,9 @@ describe("QuickEntryBox", () => {
});
});
- it("creates task on Enter key with TaskCreateInput", async () => {
+ // FN-7591: QuickEntryBox must not force column:"triage" — the store resolves the landing column from
+ // the (selected or default) workflow's intake column, so a manual-intake workflow parks the card instead.
+ it("creates task on Enter key with TaskCreateInput and without forcing column:triage", async () => {
const { props } = renderQuickEntryBox({});
const textarea = screen.getByTestId("quick-entry-input");
@@ -1384,9 +1386,10 @@ describe("QuickEntryBox", () => {
expect(props.onCreate).toHaveBeenCalledWith(
expect.objectContaining({
description: "New task description",
- column: "triage",
}),
);
+ const submitted = vi.mocked(props.onCreate).mock.calls[0][0];
+ expect(submitted.column).toBeUndefined();
});
});
@@ -1639,6 +1642,8 @@ describe("QuickEntryBox", () => {
fireEvent.change(screen.getByTestId("quick-entry-input"), { target: { value: "Create in selected workflow" } });
clickSave();
await waitFor(() => expect(onCreate).toHaveBeenCalledWith(expect.objectContaining({ workflowId: "wf-default" })));
+ // FN-7591: forwarding workflowId at create time must not carry a hard-coded column:"triage".
+ expect(vi.mocked(onCreate).mock.calls[0][0].column).toBeUndefined();
expect(screen.queryByTestId("plan-button")).not.toBeInTheDocument();
expect(onPlanningMode).not.toHaveBeenCalled();
@@ -3722,7 +3727,7 @@ describe("QuickEntryBox", () => {
expect(localStorage.getItem(QUICK_ENTRY_STORAGE_KEY)).toBeNull();
});
- it("clicking save action creates the task", async () => {
+ it("clicking save action creates the task without forcing column:triage", async () => {
const { props } = renderQuickEntryBox({});
expandQuickEntry();
const textarea = screen.getByTestId("quick-entry-input");
@@ -3734,9 +3739,10 @@ describe("QuickEntryBox", () => {
expect(props.onCreate).toHaveBeenCalledWith(
expect.objectContaining({
description: "Task to save",
- column: "triage",
}),
);
+ const submitted = vi.mocked(props.onCreate).mock.calls[0][0];
+ expect(submitted.column).toBeUndefined();
});
});
diff --git a/packages/dashboard/app/components/__tests__/TodoView.test.tsx b/packages/dashboard/app/components/__tests__/TodoView.test.tsx
index 2c1f06f4bc..0574ea136f 100644
--- a/packages/dashboard/app/components/__tests__/TodoView.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TodoView.test.tsx
@@ -446,7 +446,9 @@ describe("TodoView", () => {
expect(mockCreateTask).not.toHaveBeenCalled();
});
- it("clicking Create Task button calls createTask with item text", async () => {
+ // FN-7591: TodoView create handlers must not force column:"triage" — the store resolves the landing
+ // column from the project-default workflow's intake column instead.
+ it("clicking Create Task button calls createTask with item text and without forcing column:triage", async () => {
const onTaskCreated = vi.fn();
mockCreateTask.mockResolvedValueOnce({ id: "FN-123" });
render();
@@ -455,7 +457,7 @@ describe("TodoView", () => {
await waitFor(() => {
expect(mockCreateTask).toHaveBeenCalledWith(
- { description: "Buy groceries", column: "triage", source: { sourceType: "dashboard_ui" } },
+ { description: "Buy groceries", source: { sourceType: "dashboard_ui" } },
"project-1",
);
});
@@ -474,7 +476,7 @@ describe("TodoView", () => {
expect(screen.getByText("Builder")).toBeInTheDocument();
});
- it("selecting an agent creates task assigned to that agent", async () => {
+ it("selecting an agent creates task assigned to that agent without forcing column:triage", async () => {
const onTaskCreated = vi.fn();
mockCreateTask.mockResolvedValueOnce({ id: "FN-234" });
render();
@@ -486,7 +488,7 @@ describe("TodoView", () => {
await waitFor(() => {
expect(mockCreateTask).toHaveBeenCalledWith(
- { description: "Buy groceries", column: "triage", assignedAgentId: "agent-1", source: { sourceType: "dashboard_ui" } },
+ { description: "Buy groceries", assignedAgentId: "agent-1", source: { sourceType: "dashboard_ui" } },
"project-1",
);
});
diff --git a/packages/dashboard/app/components/__tests__/board-quickcreate-workflow-lane-visibility.test.tsx b/packages/dashboard/app/components/__tests__/board-quickcreate-workflow-lane-visibility.test.tsx
index 1f76f1c3f1..28964c172a 100644
--- a/packages/dashboard/app/components/__tests__/board-quickcreate-workflow-lane-visibility.test.tsx
+++ b/packages/dashboard/app/components/__tests__/board-quickcreate-workflow-lane-visibility.test.tsx
@@ -94,6 +94,22 @@ const CUSTOM_WORKFLOW = {
],
};
+/*
+FNXC:CodingIdeasWorkflow 2026-07-05-00:00:
+A task created under the Coding (Ideas) workflow (manual "ideas" intake, autoTriage:false) must render in the board's
+"ideas" lane, not "triage" — mirrors the real builtin:coding-ideas workflow's intake column id/flag shape.
+*/
+const CODING_IDEAS_WORKFLOW = {
+ id: "builtin:coding-ideas",
+ name: "Coding (Ideas)",
+ columns: [
+ { id: "ideas", name: "Ideas", flags: { intake: true } },
+ { id: "todo", name: "Todo", flags: { hold: true } },
+ { id: "done", name: "Done", flags: { complete: true } },
+ { id: "archived", name: "Archived", flags: { archived: true } },
+ ],
+};
+
function mkTask(overrides: Partial & { id: string }): Task {
return {
title: overrides.id,
@@ -113,7 +129,7 @@ function workflowPayload(taskWorkflowIds: Record, flagEnabled =
return {
flagEnabled,
defaultWorkflowId: DEFAULT_WORKFLOW.id,
- workflows: flagEnabled ? [DEFAULT_WORKFLOW, CUSTOM_WORKFLOW] : [],
+ workflows: flagEnabled ? [DEFAULT_WORKFLOW, CUSTOM_WORKFLOW, CODING_IDEAS_WORKFLOW] : [],
taskWorkflowIds,
};
}
@@ -269,6 +285,33 @@ describe("workflow lane quick-create visibility", () => {
expect(screen.getByText(title)).toBeTruthy();
});
+ it("Board renders a task created under the Coding (Ideas) workflow in the ideas lane", async () => {
+ const refetch = deferred();
+ fetchBoardWorkflowsMock
+ .mockResolvedValueOnce(workflowPayload({}))
+ .mockResolvedValueOnce(workflowPayload({}))
+ .mockReturnValueOnce(refetch.promise);
+
+ render();
+ await screen.findByTestId("workflow-switcher");
+ selectWorkflow(CODING_IDEAS_WORKFLOW.id);
+
+ await act(async () => {
+ fireEvent.click(screen.getByTestId("quick-create-ideas"));
+ });
+
+ const ideasColumn = screen.getByTestId("column-ideas");
+ expect(within(ideasColumn).getByText("Created builtin:coding-ideas")).toBeTruthy();
+ expect(JSON.parse(ideasColumn.getAttribute("data-task-ids") ?? "[]")).toContain("FN-new");
+
+ await act(async () => {
+ refetch.resolve(workflowPayload({ "FN-new": CODING_IDEAS_WORKFLOW.id }));
+ await refetch.promise;
+ });
+
+ expect(within(screen.getByTestId("column-ideas")).getByText("Created builtin:coding-ideas")).toBeTruthy();
+ });
+
it("leaves the legacy flag-off Board quick-create path unchanged", async () => {
const inputs: TaskCreateInput[] = [];
fetchBoardWorkflowsMock.mockResolvedValue(workflowPayload({}, false));
diff --git a/packages/dashboard/app/hooks/__tests__/useTaskHandlers.test.ts b/packages/dashboard/app/hooks/__tests__/useTaskHandlers.test.ts
index d5cb65a18e..d1c59a3a9a 100644
--- a/packages/dashboard/app/hooks/__tests__/useTaskHandlers.test.ts
+++ b/packages/dashboard/app/hooks/__tests__/useTaskHandlers.test.ts
@@ -37,7 +37,10 @@ describe("useTaskHandlers", () => {
vi.clearAllMocks();
});
- it("handleBoardQuickCreate calls createTask with triage column and returns task", async () => {
+ // FN-7591: handleBoardQuickCreate/handleModalCreate must NOT force column:"triage" — the store resolves the
+ // landing column from the (selected or default) workflow's intake column, so a manual-intake workflow
+ // (e.g. Coding (Ideas) → "ideas") parks the card instead of being auto-triaged.
+ it("handleBoardQuickCreate forwards createTask without forcing a column", async () => {
const options = createOptions();
const { result } = renderHook(() => useTaskHandlers(options));
const input: TaskCreateInput = { description: "Do work" };
@@ -47,11 +50,27 @@ describe("useTaskHandlers", () => {
created = await result.current.handleBoardQuickCreate(input);
});
- expect(options.createTask).toHaveBeenCalledWith({ description: "Do work", column: "triage", source: { sourceType: "dashboard_ui" } });
+ expect(options.createTask).toHaveBeenCalledWith({ description: "Do work", source: { sourceType: "dashboard_ui" } });
expect(created).toEqual(CREATED_TASK);
});
- it("handleModalCreate calls createTask with triage column and returns task", async () => {
+ it("handleBoardQuickCreate forwards an explicit workflowId without forcing a column", async () => {
+ const options = createOptions();
+ const { result } = renderHook(() => useTaskHandlers(options));
+ const input: TaskCreateInput = { description: "Do work", workflowId: "builtin:coding-ideas" };
+
+ await act(async () => {
+ await result.current.handleBoardQuickCreate(input);
+ });
+
+ expect(options.createTask).toHaveBeenCalledWith({
+ description: "Do work",
+ workflowId: "builtin:coding-ideas",
+ source: { sourceType: "dashboard_ui" },
+ });
+ });
+
+ it("handleModalCreate forwards createTask without forcing a column", async () => {
const options = createOptions();
const { result } = renderHook(() => useTaskHandlers(options));
@@ -60,7 +79,7 @@ describe("useTaskHandlers", () => {
created = await result.current.handleModalCreate({ description: "From modal" });
});
- expect(options.createTask).toHaveBeenCalledWith({ description: "From modal", column: "triage", source: { sourceType: "dashboard_ui" } });
+ expect(options.createTask).toHaveBeenCalledWith({ description: "From modal", source: { sourceType: "dashboard_ui" } });
expect(created).toEqual(CREATED_TASK);
});
diff --git a/packages/dashboard/app/hooks/useTaskHandlers.ts b/packages/dashboard/app/hooks/useTaskHandlers.ts
index 5593e0f717..51fddf72df 100644
--- a/packages/dashboard/app/hooks/useTaskHandlers.ts
+++ b/packages/dashboard/app/hooks/useTaskHandlers.ts
@@ -32,16 +32,20 @@ export function useTaskHandlers(options: UseTaskHandlersOptions): UseTaskHandler
addToast,
} = options;
+ /*
+ FNXC:CodingIdeasWorkflow 2026-07-05-00:00:
+ These wrappers previously forced `column: "triage"` (handleBoardQuickCreate defaulted to it when the caller omitted column; handleModalCreate hard-coded it unconditionally), which overrode InlineCreateCard/NewTaskModal even after those callers stopped sending an explicit column. Both must now forward the caller's `column` untouched (usually omitted) so the store resolves the landing column from the (selected or default) workflow's intake column — e.g. Coding (Ideas) → "ideas" — instead of always forcing legacy triage.
+ */
const handleBoardQuickCreate = useCallback(
async (input: TaskCreateInput): Promise => {
- return createTask({ ...input, column: input.column ?? "triage", source: { sourceType: "dashboard_ui" } });
+ return createTask({ ...input, source: { sourceType: "dashboard_ui" } });
},
[createTask],
);
const handleModalCreate = useCallback(
async (input: TaskCreateInput): Promise => {
- const task = await createTask({ ...input, column: "triage", source: { sourceType: "dashboard_ui" } });
+ const task = await createTask({ ...input, source: { sourceType: "dashboard_ui" } });
return task;
},
[createTask],