diff --git a/.changeset/fn-6532-chat-first-task-detail.md b/.changeset/fn-6532-chat-first-task-detail.md
new file mode 100644
index 0000000000..b74868f798
--- /dev/null
+++ b/.changeset/fn-6532-chat-first-task-detail.md
@@ -0,0 +1,5 @@
+---
+"@runfusion/fusion": patch
+---
+
+Make Chat the first tab and default active view in the task detail modal while preserving explicit initial tab requests.
diff --git a/packages/dashboard/app/components/TaskDetailModal.tsx b/packages/dashboard/app/components/TaskDetailModal.tsx
index 9198613c74..a3a402372b 100644
--- a/packages/dashboard/app/components/TaskDetailModal.tsx
+++ b/packages/dashboard/app/components/TaskDetailModal.tsx
@@ -375,7 +375,7 @@ export interface TaskDetailModalProps {
prAuthAvailable?: boolean;
autoMergeEnabled?: boolean;
onOpenWorkflowEditor?: () => void;
- /** Open the modal with this tab active instead of "definition" */
+ /** Open the modal with this tab active instead of the default Chat view. */
initialTab?: TabId;
/** Mobile-only header affordance mode. */
mobileHeaderMode?: "close" | "back";
@@ -555,7 +555,11 @@ export function TaskDetailContent({
prAuthAvailable,
autoMergeEnabled: autoMergeEnabledProp,
onOpenWorkflowEditor,
- initialTab = "definition",
+ /**
+ * FNXC:TaskDetailTabs 2026-06-17-00:00:
+ * FN-6532 makes Chat the default task-detail view when no caller supplies an explicit initial tab.
+ */
+ initialTab = "chat",
mobileHeaderMode = "close",
embedded = false,
onRequestClose,
@@ -3063,18 +3067,22 @@ export function TaskDetailContent({
{!isEditing && (
<>
- setActiveTab("definition")}
- >
- {t("taskDetail.tabs.definition", "Definition")}
-
+ {/*
+ FNXC:TaskDetailTabs 2026-06-17-00:00:
+ FN-6532 requires Chat to be the first task-detail tab while preserving every explicit tab entrypoint.
+ */}
setActiveTab("chat")}
>
{t("taskDetail.tabs.chat", "Chat")}
+ setActiveTab("definition")}
+ >
+ {t("taskDetail.tabs.definition", "Definition")}
+
setActiveTab("logs")}
diff --git a/packages/dashboard/app/components/__tests__/AppModals.test.tsx b/packages/dashboard/app/components/__tests__/AppModals.test.tsx
index dcfe4ba272..b866c8ec31 100644
--- a/packages/dashboard/app/components/__tests__/AppModals.test.tsx
+++ b/packages/dashboard/app/components/__tests__/AppModals.test.tsx
@@ -162,7 +162,7 @@ describe("AppModals", () => {
const mockModalManager: ModalManager = {
// State
detailTask: null,
- detailTaskInitialTab: "definition",
+ detailTaskInitialTab: "chat",
settingsOpen: false,
settingsInitialSection: undefined,
githubImportOpen: false,
diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx
index 89849e3308..f40c4505a9 100644
--- a/packages/dashboard/app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.attachments-and-tabs.test.tsx
@@ -75,6 +75,7 @@ describe("TaskDetailModal", () => {
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
});
describe("tab toggle", () => {
- it("defaults to the Definition tab", () => {
+ it("defaults to the Chat tab", () => {
const { container } = render(
{
expect(screen.getByText("Definition")).toBeTruthy();
expect(screen.getByText("Logs")).toBeTruthy();
- // Activity and Agent Log are subviews inside the Logs tab, not top-level tabs
- // They should NOT be visible on the Definition tab
+ // Activity and Agent Log are subviews inside the Logs tab, not top-level tabs.
+ // They should NOT be visible on the default Chat tab.
expect(screen.queryByText("Activity")).toBeNull();
expect(screen.queryByText("Agent Log")).toBeNull();
- // Definition content should be visible
- expect(container.querySelector(".markdown-body")).toBeTruthy();
- // Activity section should NOT be visible initially
+ // Chat content should be visible by default.
+ expect(container.querySelector(".detail-section--chat")).toBeTruthy();
+ expect(container.querySelector("[data-testid='task-chat-tab']")).toBeTruthy();
+ // Activity section should NOT be visible initially.
expect(container.querySelector(".detail-activity")).toBeNull();
- // Agent log viewer should not be visible
+ // Agent log viewer should not be visible.
expect(container.querySelector("[data-testid='agent-log-viewer']")).toBeNull();
- // After clicking Logs tab, the subview toggle buttons should appear
+ // After clicking Logs tab, the subview toggle buttons should appear.
fireEvent.click(screen.getByText("Logs"));
const logSubviewToggle = container.querySelector(".log-subview-toggle");
expect(logSubviewToggle).toBeTruthy();
@@ -601,6 +610,7 @@ describe("TaskDetailModal", () => {
prompt: "# Hello\n\nContent",
log: [{ timestamp: "2026-01-01T00:00:00Z", action: "Test" }],
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -691,7 +701,7 @@ describe("TaskDetailModal", () => {
/>,
);
- // Default: Definition tab active → enabled should be false
+ // Default: Chat tab active → enabled should be false
const initialCall = mockUseAgentLogs.mock.calls[mockUseAgentLogs.mock.calls.length - 1];
expect(initialCall[1]).toBe(false);
@@ -744,15 +754,15 @@ describe("TaskDetailModal", () => {
);
// For an in-progress task (no workflow steps, no merge commit), the
- // top-level tabs are: Definition, Chat, Logs, Changes, Review, Comments,
+ // top-level tabs are: Chat, Definition, Logs, Changes, Review, Comments,
// Documents, Model, Workflow, Stats, Routing.
- const tabTexts = ["Definition", "Chat", "Logs", "Changes", "Review", "Comments", "Documents", "Model", "Workflow", "Stats", "Routing"];
+ const tabTexts = ["Chat", "Definition", "Logs", "Changes", "Review", "Comments", "Documents", "Model", "Workflow", "Stats", "Routing"];
const tabs = screen.getAllByRole("button").filter((b) =>
tabTexts.includes(b.textContent || "")
);
expect(tabs.map((tab) => tab.textContent)).toEqual(tabTexts);
- expect(tabs[0].textContent).toBe("Definition");
- expect(tabs[1].textContent).toBe("Chat");
+ expect(tabs[0].textContent).toBe("Chat");
+ expect(tabs[1].textContent).toBe("Definition");
expect(tabs[2].textContent).toBe("Logs");
// Activity and Agent Log are NOT top-level tabs (they are subviews inside Logs)
@@ -937,6 +947,47 @@ describe("TaskDetailModal", () => {
expect(screen.queryByTestId("task-chat-expand-toggle")).toBeNull();
});
+ it("FN-6532 defaults to Chat first while preserving explicit tab requests", () => {
+ const { container, rerender } = render(
+ ,
+ );
+
+ const tabs = Array.from(container.querySelectorAll(".detail-tab"));
+ expect(tabs.map((tab) => tab.textContent)).toEqual(expect.arrayContaining(["Chat", "Definition"]));
+ expect(tabs[0]).toHaveTextContent("Chat");
+ const chatTab = screen.getByRole("button", { name: "Chat" });
+ const definitionTab = screen.getByRole("button", { name: "Definition" });
+ expect(chatTab.compareDocumentPosition(definitionTab) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
+ expect(chatTab).toHaveClass("detail-tab-active");
+ expect(definitionTab).not.toHaveClass("detail-tab-active");
+ expect(container.querySelector(".detail-section--chat [data-testid='task-chat-tab']")).toBeTruthy();
+
+ rerender(
+ ,
+ );
+
+ expect(screen.getByRole("button", { name: "Logs" })).toHaveClass("detail-tab-active");
+ expect(screen.getByRole("button", { name: "Chat" })).not.toHaveClass("detail-tab-active");
+ expect(container.querySelector(".detail-section--chat")).toBeNull();
+ });
+
it("FN-6347 applies chat modifiers only while the Chat tab is active", () => {
const { container } = render(
{
/>,
);
- expect(container.querySelector(".detail-body--chat")).toBeNull();
- expect(container.querySelector(".detail-section--chat")).toBeNull();
-
- fireEvent.click(screen.getByRole("button", { name: "Chat" }));
const chatBody = container.querySelector(".detail-body--chat");
const chatSection = container.querySelector(".detail-section--chat");
expect(chatBody).toBeTruthy();
diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.definition-actions.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.definition-actions.test.tsx
index 04c15203fd..29b36b7507 100644
--- a/packages/dashboard/app/components/__tests__/TaskDetailModal.definition-actions.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.definition-actions.test.tsx
@@ -24,6 +24,7 @@ describe("TaskDetailModal", () => {
render(
{
const { container } = render(
{
const { container } = render(
{
const { container } = render(
{
render(
{
render(
{
const { container } = render(
{
);
// In-progress tasks show exactly 11 tabs:
- // Definition, Chat, Logs, Changes, Review, Comments, Documents, Model, Workflow, Stats, Routing
+ // Chat, Definition, Logs, Changes, Review, Comments, Documents, Model, Workflow, Stats, Routing
const tabs = container.querySelectorAll(".detail-tab");
expect(tabs.length).toBe(11);
- expect(tabs[0].textContent).toBe("Definition");
- expect(tabs[1].textContent).toBe("Chat");
+ expect(tabs[0].textContent).toBe("Chat");
+ expect(tabs[1].textContent).toBe("Definition");
expect(tabs[2].textContent).toBe("Logs");
expect(tabs[3].textContent).toBe("Changes");
expect(tabs[4].textContent).toBe("Review");
@@ -205,6 +212,7 @@ describe("TaskDetailModal", () => {
const { container } = render(
{
// In-progress task with workflow steps: 11 tabs (Review after Changes, Workflow after Model)
const tabs = container.querySelectorAll(".detail-tab");
expect(tabs.length).toBe(11);
- expect(tabs[0].textContent).toBe("Definition");
- expect(tabs[1].textContent).toBe("Chat");
+ expect(tabs[0].textContent).toBe("Chat");
+ expect(tabs[1].textContent).toBe("Definition");
expect(tabs[2].textContent).toBe("Logs");
expect(tabs[3].textContent).toBe("Changes");
expect(tabs[4].textContent).toBe("Review");
@@ -237,6 +245,7 @@ describe("TaskDetailModal", () => {
column: "done",
mergeDetails: { commitSha: "abc1234567890", filesChanged: 3 },
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -246,11 +255,11 @@ describe("TaskDetailModal", () => {
/>,
);
- // Done task with commit SHA: Definition, Chat, Logs, Changes, Review, Comments, Documents, Model, Workflow, Stats, Routing (11 tabs, no Commits)
+ // Done task with commit SHA: Chat, Definition, Logs, Changes, Review, Comments, Documents, Model, Workflow, Stats, Routing (11 tabs, no Commits)
const tabs = container.querySelectorAll(".detail-tab");
expect(tabs.length).toBe(11);
- expect(tabs[0].textContent).toBe("Definition");
- expect(tabs[1].textContent).toBe("Chat");
+ expect(tabs[0].textContent).toBe("Chat");
+ expect(tabs[1].textContent).toBe("Definition");
expect(tabs[2].textContent).toBe("Logs");
expect(tabs[3].textContent).toBe("Changes");
expect(tabs[4].textContent).toBe("Review");
@@ -272,6 +281,7 @@ describe("TaskDetailModal", () => {
mergeDetails: { commitSha: "abc1234567890", filesChanged: 3 },
enabledWorkflowSteps: ["WS-001"],
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -284,8 +294,8 @@ describe("TaskDetailModal", () => {
// Done task with workflow steps and commit SHA: 11 tabs including Review (no Commits)
const tabs = container.querySelectorAll(".detail-tab");
expect(tabs.length).toBe(11);
- expect(tabs[0].textContent).toBe("Definition");
- expect(tabs[1].textContent).toBe("Chat");
+ expect(tabs[0].textContent).toBe("Chat");
+ expect(tabs[1].textContent).toBe("Definition");
expect(tabs[2].textContent).toBe("Logs");
expect(tabs[3].textContent).toBe("Changes");
expect(tabs[4].textContent).toBe("Review");
@@ -303,6 +313,7 @@ describe("TaskDetailModal", () => {
const { container: triageContainer } = render(
{
);
const triageTabs = triageContainer.querySelectorAll(".detail-tab");
- expect(triageTabs.length).toBe(10); // Definition, Chat, Logs, Review, Comments, Documents, Model, Workflow, Stats, Routing
+ expect(triageTabs.length).toBe(10); // Chat, Definition, Logs, Review, Comments, Documents, Model, Workflow, Stats, Routing
expect(Array.from(triageTabs).map(t => t.textContent)).toEqual([
- "Definition", "Chat", "Logs", "Review", "Comments", "Documents", "Model", "Workflow", "Stats", "Routing",
+ "Chat", "Definition", "Logs", "Review", "Comments", "Documents", "Model", "Workflow", "Stats", "Routing",
]);
const { container: todoContainer } = render(
{
);
const todoTabs = todoContainer.querySelectorAll(".detail-tab");
- expect(todoTabs.length).toBe(10); // Definition, Chat, Logs, Review, Comments, Documents, Model, Workflow, Stats, Routing
+ expect(todoTabs.length).toBe(10); // Chat, Definition, Logs, Review, Comments, Documents, Model, Workflow, Stats, Routing
expect(Array.from(todoTabs).map(t => t.textContent)).toEqual([
- "Definition", "Chat", "Logs", "Review", "Comments", "Documents", "Model", "Workflow", "Stats", "Routing",
+ "Chat", "Definition", "Logs", "Review", "Comments", "Documents", "Model", "Workflow", "Stats", "Routing",
]);
});
@@ -341,6 +353,7 @@ describe("TaskDetailModal", () => {
render(
{
status: "awaiting-approval",
prompt: "# Task Spec",
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -385,6 +399,7 @@ describe("TaskDetailModal", () => {
status: "awaiting-approval",
prompt: "# Task Spec",
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -406,6 +421,7 @@ describe("TaskDetailModal", () => {
status: "planning",
prompt: "# Task Spec",
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -427,6 +443,7 @@ describe("TaskDetailModal", () => {
status: "awaiting-approval",
prompt: "",
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -454,6 +471,7 @@ describe("TaskDetailModal", () => {
status: "awaiting-approval",
prompt: "# Task Spec",
})}
+ initialTab="definition"
onClose={onClose}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -489,6 +507,7 @@ describe("TaskDetailModal", () => {
status: "awaiting-approval",
prompt: "# Task Spec",
})}
+ initialTab="definition"
onClose={onClose}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -534,6 +553,7 @@ describe("TaskDetailModal", () => {
status: "awaiting-approval",
prompt: "# Task Spec",
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -566,6 +586,7 @@ describe("TaskDetailModal", () => {
status: "awaiting-approval",
prompt: "# Task Spec",
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -600,6 +621,7 @@ describe("TaskDetailModal", () => {
status: "awaiting-approval",
prompt: "# Task Spec",
})}
+ initialTab="definition"
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
@@ -623,6 +645,7 @@ describe("TaskDetailModal", () => {
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
const { container } = render(
{
render(
{
const { container } = render(
{
});
expect(result.current.detailTask?.id).toBe("FN-123");
- expect(result.current.detailTaskInitialTab).toBe("definition");
+ expect(result.current.detailTaskInitialTab).toBe("chat");
act(() => {
result.current.openDetailWithChangesTab(task);
@@ -203,7 +203,7 @@ describe("useModalManager", () => {
expect(result.current.detailTask?.id).toBe("FN-456");
// Should not have prompt field (plain Task)
expect("prompt" in (result.current.detailTask as unknown as Record)).toBe(false);
- expect(result.current.detailTaskInitialTab).toBe("definition");
+ expect(result.current.detailTaskInitialTab).toBe("chat");
});
it.each([
diff --git a/packages/dashboard/app/hooks/useModalManager.ts b/packages/dashboard/app/hooks/useModalManager.ts
index 924a46a45e..28a4d1f754 100644
--- a/packages/dashboard/app/hooks/useModalManager.ts
+++ b/packages/dashboard/app/hooks/useModalManager.ts
@@ -5,6 +5,7 @@ import type { SectionId } from "../components/SettingsModal";
import type { ToastType } from "./useToast";
export type DetailTaskTab =
+ | "chat"
| "definition"
| "logs"
| "changes"
@@ -162,7 +163,11 @@ export function useModalManager(options: UseModalManagerOptions): ModalManager {
const [subtaskResumeSessionId, setSubtaskResumeSessionId] = useState(undefined);
// Can be Task (optimistic open) or TaskDetail (full data with prompt)
const [detailTask, setDetailTask] = useState<(Task | TaskDetail) | null>(null);
- const [detailTaskInitialTab, setDetailTaskInitialTab] = useState("definition");
+ /**
+ * FNXC:TaskDetailTabs 2026-06-17-00:00:
+ * FN-6532 makes Chat the default task-detail view whenever a task opens without an explicit tab request.
+ */
+ const [detailTaskInitialTab, setDetailTaskInitialTab] = useState("chat");
const [detailTaskOrigin, setDetailTaskOrigin] = useState(null);
const [groupModalGroupId, setGroupModalGroupId] = useState(null);
const [settingsOpen, setSettingsOpen] = useState(false);
@@ -250,9 +255,13 @@ export function useModalManager(options: UseModalManagerOptions): ModalManager {
setSubtaskResumeSessionId(undefined);
}, []);
+ /**
+ * FNXC:TaskDetailTabs 2026-06-17-00:00:
+ * Open-detail callers that omit initialTab should land on Chat; explicit tab requests preserve caller intent.
+ */
const openDetailTask = useCallback((
task: Task | TaskDetail,
- initialTab: DetailTaskTab = "definition",
+ initialTab: DetailTaskTab = "chat",
options?: { origin?: DetailTaskOrigin },
) => {
setDetailTask(task);