diff --git a/apps/web/app/office/agents/[id]/runs/components/tasks-touched.tsx b/apps/web/app/office/agents/[id]/runs/components/tasks-touched.tsx
index f8aaf2182e..4f9314c623 100644
--- a/apps/web/app/office/agents/[id]/runs/components/tasks-touched.tsx
+++ b/apps/web/app/office/agents/[id]/runs/components/tasks-touched.tsx
@@ -7,7 +7,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
import { getTask } from "@/lib/api/domains/office-extended-api";
import type { OfficeTask } from "@/lib/state/slices/office/types";
import { StatusIcon } from "@/app/office/tasks/status-icon";
-import { normalizeTaskStatus } from "@/app/office/tasks/normalize-status";
+import { normalizeTaskStatus } from "@/lib/api/domains/office-task-normalize";
import { PRIORITY_LABEL_KEYS, STATUS_LABEL_KEYS } from "@/app/office/lib/label-keys";
import { useTranslation } from "react-i18next";
diff --git a/apps/web/app/office/tasks/[id]/page.test.ts b/apps/web/app/office/tasks/[id]/page.test.ts
new file mode 100644
index 0000000000..dfbda38c7f
--- /dev/null
+++ b/apps/web/app/office/tasks/[id]/page.test.ts
@@ -0,0 +1,33 @@
+import { describe, expect, it } from "vitest";
+import { mapOfficeTaskToTask } from "./page";
+import type { OfficeTask } from "@/lib/state/slices/office/types";
+
+const baseRaw: OfficeTask = {
+ id: "task-1",
+ workspaceId: "workspace-1",
+ identifier: "E2E-1",
+ title: "Mapper regression task",
+ status: "todo",
+ priority: "medium",
+ createdAt: "2026-05-01T10:00:00Z",
+ updatedAt: "2026-05-01T10:00:00Z",
+};
+
+describe("mapOfficeTaskToTask", () => {
+ // Regression: the detail page seeds its Task from the store-held OfficeTask
+ // before the API GET resolves (page.tsx's useIssueData). Dropping rawStatus
+ // here silently breaks ExecutionIndicator's Live/Ready sub-state distinction
+ // for however long the seeded task is shown — see task-advanced-mode.test.tsx
+ // and OfficeSimplePane.test.tsx for the consumer-level regression.
+ it("carries rawStatus through when the store holds one", () => {
+ const mapped = mapOfficeTaskToTask({ ...baseRaw, status: "todo", rawStatus: "SCHEDULING" });
+
+ expect(mapped.rawStatus).toBe("SCHEDULING");
+ });
+
+ it("falls back to the canonical status when rawStatus is absent", () => {
+ const mapped = mapOfficeTaskToTask({ ...baseRaw, status: "in_progress" });
+
+ expect(mapped.rawStatus).toBe("in_progress");
+ });
+});
diff --git a/apps/web/app/office/tasks/[id]/page.tsx b/apps/web/app/office/tasks/[id]/page.tsx
index e95acad65b..ad4e63010c 100644
--- a/apps/web/app/office/tasks/[id]/page.tsx
+++ b/apps/web/app/office/tasks/[id]/page.tsx
@@ -58,7 +58,7 @@ function mapDecisionDTO(d: TaskDecisionDTO): TaskDecision {
};
}
-function mapOfficeTaskToTask(raw: OfficeTask): Task {
+export function mapOfficeTaskToTask(raw: OfficeTask): Task {
// The server DTO includes reviewers/approvers/decisions even though the
// strongly-typed OfficeTask only declares the cross-cutting fields. We
// read those extra props off the raw object.
@@ -75,6 +75,7 @@ function mapOfficeTaskToTask(raw: OfficeTask): Task {
title: raw.title,
description: raw.description,
status: raw.status as Task["status"],
+ rawStatus: raw.rawStatus ?? raw.status,
priority: (raw.priority || "medium") as Task["priority"],
labels: (raw.labels ?? []).map((l) =>
typeof l === "string" ? { name: l, color: "#6b7280" } : l,
diff --git a/apps/web/app/office/tasks/[id]/task-advanced-mode.test.tsx b/apps/web/app/office/tasks/[id]/task-advanced-mode.test.tsx
new file mode 100644
index 0000000000..f89120afce
--- /dev/null
+++ b/apps/web/app/office/tasks/[id]/task-advanced-mode.test.tsx
@@ -0,0 +1,100 @@
+import { afterEach, describe, expect, it, vi } from "vitest";
+import { cleanup, render, screen } from "@testing-library/react";
+import type { Task } from "./types";
+
+vi.mock("@/lib/routing/client-dynamic", () => ({
+ default: () =>
+ function MockOfficeDockviewLayout() {
+ return null;
+ },
+}));
+
+vi.mock("@/components/routing/app-link", () => ({
+ default: ({ children, href, ...props }: { children: React.ReactNode; href: string }) => (
+
+ {children}
+
+ ),
+}));
+
+vi.mock("./status-icon", () => ({
+ StatusIcon: () => null,
+}));
+
+vi.mock("./use-advanced-session", () => ({
+ useAdvancedSession: () => ({ sessionId: null, isSessionEnded: false }),
+}));
+
+vi.mock("../../components/execution-indicator", () => ({
+ ExecutionIndicator: ({ status }: { status: string }) => (
+
{status}
+ ),
+}));
+
+vi.mock("../../components/office-topbar-portal", () => ({
+ OfficeTopbarPortal: ({ children }: { children: React.ReactNode }) => {children}
,
+}));
+
+vi.mock("@kandev/ui/label", () => ({
+ Label: ({ children }: { children: React.ReactNode }) => ,
+}));
+
+vi.mock("@kandev/ui/switch", () => ({
+ Switch: () => null,
+}));
+
+import { TaskAdvancedMode } from "./task-advanced-mode";
+
+const baseTask: Task = {
+ id: "task-1",
+ workspaceId: "workspace-1",
+ identifier: "E2E-1",
+ title: "Advanced mode task",
+ status: "todo",
+ priority: "medium",
+ labels: [],
+ blockedBy: [],
+ blocking: [],
+ children: [],
+ reviewers: [],
+ approvers: [],
+ decisions: [],
+ createdBy: "",
+ createdAt: "2026-05-01T10:00:00Z",
+ updatedAt: "2026-05-01T10:00:00Z",
+};
+
+afterEach(() => cleanup());
+
+describe("TaskAdvancedMode ExecutionIndicator wiring", () => {
+ // Regression: the detail page's store-seeded task must feed ExecutionIndicator
+ // the raw backend status, not the normalized canonical one, so it still tells
+ // SCHEDULING (live) apart from WAITING_FOR_INPUT (not live) — the same
+ // distinction task-row.tsx already preserves for the board/list views.
+ it("prefers rawStatus over status so a SCHEDULING task still shows Live", () => {
+ render(
+ {}}
+ />,
+ );
+ expect(screen.getByTestId("execution-indicator").textContent).toBe("SCHEDULING");
+ });
+
+ it("prefers rawStatus over status so a WAITING_FOR_INPUT task does not falsely show Live", () => {
+ render(
+ {}}
+ />,
+ );
+ expect(screen.getByTestId("execution-indicator").textContent).toBe("WAITING_FOR_INPUT");
+ });
+
+ it("falls back to status when rawStatus is absent", () => {
+ render(
+ {}} />,
+ );
+ expect(screen.getByTestId("execution-indicator").textContent).toBe("in_progress");
+ });
+});
diff --git a/apps/web/app/office/tasks/[id]/task-advanced-mode.tsx b/apps/web/app/office/tasks/[id]/task-advanced-mode.tsx
index 8e2f454259..a0e92ea60e 100644
--- a/apps/web/app/office/tasks/[id]/task-advanced-mode.tsx
+++ b/apps/web/app/office/tasks/[id]/task-advanced-mode.tsx
@@ -33,7 +33,7 @@ export function TaskAdvancedMode({ task, onToggleSimple }: TaskAdvancedModeProps
{task.identifier}
{task.title}
-
+