diff --git a/.changeset/fn-7935-mailbox-artifact-view-task-popout.md b/.changeset/fn-7935-mailbox-artifact-view-task-popout.md
new file mode 100644
index 0000000000..9f77735673
--- /dev/null
+++ b/.changeset/fn-7935-mailbox-artifact-view-task-popout.md
@@ -0,0 +1,7 @@
+---
+"@runfusion/fusion": patch
+---
+
+summary: Mailbox artifact "View task" now opens the same movable, resizable task window used elsewhere.
+category: fix
+dev: MainContent mailbox onOpenTask routes fetchTaskDetail -> popOutTaskDetail (floating-window--task-detail) instead of the docked openDetailTask modal, matching DocumentsView's artifact-task path.
diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md
index c4fda79385..93a6877780 100644
--- a/docs/dashboard-guide.md
+++ b/docs/dashboard-guide.md
@@ -632,7 +632,7 @@ Mailbox view shows inbox/outbox communication threads and unread state.
- Inbox renders one row per message (no sender-based collapsing)
- clicking a message in the Mail tab opens the task detail pane with full message content and conversation context
- reply rows in the mailbox modal can expand inline to show the replied-to message context for easier thread reading
-- when an agent or dashboard chat session registers an artifact with `fn_artifact_register`, Fusion sends a best-effort `system` → user inbox message announcing the new artifact (for example, `New image artifact registered:
`) with metadata for `artifactId`, `artifactType`, `title`, optional `mimeType`, `authorId`, and optional `taskId`; notification delivery is informational and never blocks or rolls back the artifact registration. Artifact notifications are actionable in message detail views: image artifacts show an inline preview plus **Open artifact**, while video/audio/document/other artifacts show an **Open artifact** link to the managed media URL. When `taskId` metadata is present, the same artifact block also shows **View task** so users can open the producing task detail directly from the mailbox.
+- when an agent or dashboard chat session registers an artifact with `fn_artifact_register`, Fusion sends a best-effort `system` → user inbox message announcing the new artifact (for example, `New image artifact registered: `) with metadata for `artifactId`, `artifactType`, `title`, optional `mimeType`, `authorId`, and optional `taskId`; notification delivery is informational and never blocks or rolls back the artifact registration. Artifact notifications are actionable in message detail views: image artifacts show an inline preview plus **Open artifact**, while video/audio/document/other artifacts show an **Open artifact** link to the managed media URL. When `taskId` metadata is present, the same artifact block also shows **View task** so users can open the producing task detail directly from the mailbox in the shared movable/resizable task-detail window.
- on first engine startup under Fusion `0.59.x`, each project receives one best-effort `system` inbox notice about the upcoming embedded-Postgres storage migration with the Discord help link; `metadata.kind = "postgres-migration-notice"` prevents duplicates across restarts.
- mailbox now includes an **Approvals** tab with pending and history filters (`approved` / `denied` / `completed`), approval detail context, and inline approve/deny actions for pending requests
- for approvals gated by an agent's permission policy (permanent agents and task-worker heartbeats), the Approvals detail pane renders the gated action's real payload — tool name, shell command line or structured arguments, and working directory when present — instead of only a generic "Agent gated action for ``" summary; a stateless heartbeat retrying the same gated command reuses the existing pending approval instead of creating a duplicate (FN-7609)
diff --git a/packages/dashboard/app/components/dashboard/MainContent.tsx b/packages/dashboard/app/components/dashboard/MainContent.tsx
index 98cc108e18..fbf762aff1 100644
--- a/packages/dashboard/app/components/dashboard/MainContent.tsx
+++ b/packages/dashboard/app/components/dashboard/MainContent.tsx
@@ -358,10 +358,14 @@ export function MainContent({
{
void fetchTaskDetail(taskId, currentProject?.id)
- .then((task) => openDetailTask(task as TaskDetail))
+ .then((task) => popOutTaskDetail(task))
.catch(() => addToast?.("Failed to open task", "error"));
}}
onUnreadCountChange={setMailboxUnreadCount}
diff --git a/packages/dashboard/app/components/dashboard/__tests__/MainContent.mailbox-view-task.test.tsx b/packages/dashboard/app/components/dashboard/__tests__/MainContent.mailbox-view-task.test.tsx
new file mode 100644
index 0000000000..c2ebc3746e
--- /dev/null
+++ b/packages/dashboard/app/components/dashboard/__tests__/MainContent.mailbox-view-task.test.tsx
@@ -0,0 +1,83 @@
+import { render, screen, waitFor } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import type { TaskDetail } from "@fusion/core";
+import { MainContent } from "../MainContent";
+import type { MainContentProps } from "../types";
+
+const { fetchTaskDetailMock } = vi.hoisted(() => ({
+ fetchTaskDetailMock: vi.fn(),
+}));
+
+vi.mock("../../../api", () => ({
+ fetchTaskDetail: fetchTaskDetailMock,
+}));
+
+vi.mock("../../MailboxView", () => ({
+ MailboxView: ({ onOpenTask }: { onOpenTask?: (taskId: string) => void }) => (
+
+ ),
+}));
+
+function mainContentProps(overrides: Partial = {}): MainContentProps {
+ return {
+ showBackendConnectionErrorPage: false,
+ projectsError: null,
+ t: ((key: string, fallback?: string) => fallback ?? key) as MainContentProps["t"],
+ retryingProjects: false,
+ handleRetryProjects: vi.fn(),
+ shellApi: null,
+ taskView: "mailbox",
+ modalManager: {} as MainContentProps["modalManager"],
+ handleChangeTaskView: vi.fn(),
+ refreshAppSettings: vi.fn(async () => undefined),
+ addToast: vi.fn(),
+ currentProject: { id: "project-1", name: "Project 1" } as MainContentProps["currentProject"],
+ viewMode: "project",
+ tasks: [],
+ workflowSteps: [],
+ openDetailTask: vi.fn(),
+ popOutTaskDetail: vi.fn(),
+ setMailboxUnreadCount: vi.fn(),
+ settingsLoaded: true,
+ skillsEnabled: true,
+ insightsEnabled: true,
+ researchEnabled: true,
+ evalsEnabled: true,
+ memoryEnabled: true,
+ goalsEnabled: true,
+ todosEnabled: true,
+ nodesEnabled: true,
+ capacityRiskBannerEnabled: false,
+ capacityRiskDismissed: false,
+ capacityRiskSignal: { level: "low", reasons: [] } as unknown as MainContentProps["capacityRiskSignal"],
+ ...overrides,
+ } as unknown as MainContentProps;
+}
+
+describe("MainContent mailbox artifact View task routing", () => {
+ it("opens mailbox artifact tasks in the shared popped-out task-detail window", async () => {
+ const fetchedTask = {
+ id: "FN-7935",
+ title: "Mailbox artifact task",
+ description: "Task opened from a mailbox artifact message",
+ column: "todo",
+ status: "todo",
+ dependencies: [],
+ createdAt: new Date(0).toISOString(),
+ updatedAt: new Date(0).toISOString(),
+ steps: [],
+ } as unknown as TaskDetail;
+ const openDetailTask = vi.fn();
+ const popOutTaskDetail = vi.fn();
+
+ fetchTaskDetailMock.mockResolvedValueOnce(fetchedTask);
+
+ render();
+
+ screen.getByText("Open mailbox artifact task").click();
+
+ await waitFor(() => expect(popOutTaskDetail).toHaveBeenCalledWith(fetchedTask));
+ expect(fetchTaskDetailMock).toHaveBeenCalledWith("FN-7935", "project-1");
+ expect(openDetailTask).not.toHaveBeenCalled();
+ });
+});