diff --git a/.changeset/fn-6965-list-pane-chat-layout.md b/.changeset/fn-6965-list-pane-chat-layout.md
new file mode 100644
index 0000000000..6e4e1995fa
--- /dev/null
+++ b/.changeset/fn-6965-list-pane-chat-layout.md
@@ -0,0 +1,5 @@
+---
+"@runfusion/fusion": patch
+---
+
+Stack task-detail Chat agent headers above output blocks in the List View split-pane detail pane while preserving full-width desktop chat layout.
diff --git a/packages/dashboard/app/components/TaskChatTab.css b/packages/dashboard/app/components/TaskChatTab.css
index 746a4a844c..a49c30f140 100644
--- a/packages/dashboard/app/components/TaskChatTab.css
+++ b/packages/dashboard/app/components/TaskChatTab.css
@@ -166,6 +166,18 @@ FN-6425 requires the chat expand control to stay inside the chat view as an icon
white-space: nowrap;
}
+/*
+FNXC:TaskDetailChat 2026-06-23-21:38:
+List View split-pane detail can be narrow while the global viewport is desktop-sized. Scope the compact/mobile agent-output group contract to that host so agent/provider headers stack above output blocks there without changing full-width modal, board main-panel, popped-out, or expanded chat desktop layouts.
+*/
+.list-split-detail-content .task-chat-group {
+ grid-template-columns: 1fr;
+}
+
+.list-split-detail-content .task-chat-group-header {
+ min-width: 0;
+}
+
.task-chat-group-bubbles {
display: flex;
min-width: 0;
diff --git a/packages/dashboard/app/components/__tests__/TaskChatTab.test.tsx b/packages/dashboard/app/components/__tests__/TaskChatTab.test.tsx
index 9d54fe43f4..3c098d1ff5 100644
--- a/packages/dashboard/app/components/__tests__/TaskChatTab.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TaskChatTab.test.tsx
@@ -167,6 +167,22 @@ function expectTranscriptTextOrder(...texts: string[]) {
}
}
+function expectAgentHeaderBeforeBubbles(group: HTMLElement) {
+ const header = group.querySelector(".task-chat-group-header");
+ const bubbles = group.querySelector(".task-chat-group-bubbles");
+ expect(header).not.toBeNull();
+ expect(bubbles).not.toBeNull();
+ expect(header?.compareDocumentPosition(bubbles as Element) ?? 0).toBe(Node.DOCUMENT_POSITION_FOLLOWING);
+}
+
+function renderListSplitTaskChat(task: Task = makeTask()) {
+ return render(
+
+
+
,
+ );
+}
+
function expectIdleSessionHint() {
// FNXC:TaskDetailChat 2026-06-22-21:20: The idle "No agent is working…" banner was removed per user request — idle chats stay sendable with no hint shown.
expect(screen.queryByTestId("task-chat-idle-hint")).not.toBeInTheDocument();
@@ -486,6 +502,55 @@ describe("TaskChatTab", () => {
expect(screen.queryByLabelText("Executor: model provider unknown")).not.toBeInTheDocument();
});
+ it("keeps List View split chat agent headers before text tool thinking and user output", () => {
+ mockLogs([
+ makeEntry({ agent: "executor", text: "executor compact text", timestamp: "2026-06-12T00:00:00.000Z" }),
+ makeEntry({ agent: "executor", type: "tool", text: "bash", detail: "pnpm test", timestamp: "2026-06-12T00:00:01.000Z" }),
+ makeEntry({ agent: "executor", type: "tool_result", text: "bash", detail: "ok", timestamp: "2026-06-12T00:00:02.000Z" }),
+ makeEntry({ agent: "executor", type: "thinking", text: "checking compact layout", timestamp: "2026-06-12T00:00:03.000Z" }),
+ makeEntry({ text: "fallback compact text", timestamp: "2026-06-12T00:00:05.000Z" }),
+ ]);
+
+ renderListSplitTaskChat(
+ makeTask({
+ modelProvider: "openai",
+ modelId: "gpt-4o",
+ steeringComments: [makeSteeringComment({ id: "compact-user", text: "compact user guidance", createdAt: "2026-06-12T00:00:04.000Z" })],
+ }),
+ );
+
+ const host = screen.getByTestId("list-split-detail-content");
+ const executorGroup = within(host).getByLabelText("Executor messages");
+ const fallbackGroup = within(host).getByLabelText("Agent messages");
+
+ expectAgentHeaderBeforeBubbles(executorGroup);
+ expectAgentHeaderBeforeBubbles(fallbackGroup);
+ expect(within(executorGroup).getAllByText("Executor")).toHaveLength(1);
+ expect(within(fallbackGroup).getAllByText("Agent")).toHaveLength(1);
+ expect(within(executorGroup).getByText("executor compact text")).toBeVisible();
+ expect(within(executorGroup).getByTestId("task-chat-tool-group")).toBeInTheDocument();
+ expect(within(executorGroup).getByTestId("task-chat-thinking")).toBeInTheDocument();
+ expect(within(host).getByText("compact user guidance").closest(".task-chat-user-group")).not.toBeNull();
+ expect(within(host).getByText("fallback compact text")).toBeVisible();
+ expect(document.querySelector(".task-chat-provider-icon [data-provider='openai']")).toBeTruthy();
+ expect(within(fallbackGroup).getByLabelText("Agent: model provider unknown")).toBeVisible();
+ });
+
+ it("keeps List View split chat empty and loading states free of header shells", () => {
+ mockLogs([], true);
+ const loading = renderListSplitTaskChat();
+ expect(screen.getByText(/Loading agent output/)).toBeVisible();
+ expect(document.querySelector(".task-chat-group-header")).not.toBeInTheDocument();
+ expect(document.querySelector(".task-chat-group-bubbles")).not.toBeInTheDocument();
+
+ loading.unmount();
+ mockLogs([]);
+ renderListSplitTaskChat();
+ expect(screen.getByText(/No agent output yet/)).toBeVisible();
+ expect(document.querySelector(".task-chat-group-header")).not.toBeInTheDocument();
+ expect(document.querySelector(".task-chat-group-bubbles")).not.toBeInTheDocument();
+ });
+
it("groups consecutive entries by agent role", () => {
mockLogs([
makeEntry({ agent: "executor", text: "first" }),
@@ -2413,6 +2478,31 @@ describe("TaskChatTab", () => {
expect(source).toContain("expanded={chatExpanded}");
});
+ it("stacks agent headers only inside the List View split-detail chat host", () => {
+ const css = readFileSync(resolve(__dirname, "../TaskChatTab.css"), "utf8");
+ const desktopGroupRule = getCssRuleBlock(css, ".task-chat-group");
+ const listSplitGroupRule = getCssRuleBlock(css, ".list-split-detail-content .task-chat-group");
+ const listSplitHeaderRule = getCssRuleBlock(css, ".list-split-detail-content .task-chat-group-header");
+ const mobileCss = getCssAfter(css, "@media (max-width: 768px)");
+ const mobileGroupRule = getCssRuleBlock(mobileCss, ".task-chat-group");
+
+ expect(desktopGroupRule).toContain("grid-template-columns: auto minmax(0, 1fr)");
+ expect(listSplitGroupRule).toContain("grid-template-columns: 1fr");
+ expect(listSplitHeaderRule).toContain("min-width: 0");
+ expect(mobileGroupRule).toContain("grid-template-columns: 1fr");
+ });
+
+ it("keeps List View as the only split-pane host for compact task chat", () => {
+ const listSource = readFileSync(resolve(__dirname, "../ListView.tsx"), "utf8");
+ const appSource = readFileSync(resolve(__dirname, "../../App.tsx"), "utf8");
+
+ expect(listSource).toContain('className="list-split-detail-content"');
+ expect(listSource).toContain(" {
const css = readFileSync(resolve(__dirname, "../TaskChatTab.css"), "utf8");
const groupMetaRule = getCssRuleBlock(css, ".task-chat-group-meta");