diff --git a/.changeset/FN-7492-task-card-plan-review-progress.md b/.changeset/FN-7492-task-card-plan-review-progress.md
new file mode 100644
index 0000000000..9250f398c2
--- /dev/null
+++ b/.changeset/FN-7492-task-card-plan-review-progress.md
@@ -0,0 +1,7 @@
+---
+"@runfusion/fusion": patch
+---
+
+summary: Show active Plan Review progress on triage task cards.
+category: fix
+dev: TaskCard now renders the existing progress affordance for Triage only when unified progress has active workflow work.
diff --git a/packages/dashboard/app/components/TaskCard.tsx b/packages/dashboard/app/components/TaskCard.tsx
index 1e94adaf94..ccfc4bedf2 100644
--- a/packages/dashboard/app/components/TaskCard.tsx
+++ b/packages/dashboard/app/components/TaskCard.tsx
@@ -1163,8 +1163,13 @@ function TaskCardComponent({
() => unifiedProgress.items.filter((item) => item.status === "in-progress" || item.status === "running").length,
[unifiedProgress.items],
);
+ /*
+ FNXC:TaskCardWorkflowProgress 2026-07-04-09:08:
+ Prompt Reviewer / Plan Review can run before a task leaves Triage. Show the existing card progress affordance when Triage has an actually active unified progress item, but keep enabled-only workflow steps hidden so idle review gates do not create false active indicators or empty progress shells.
+ */
const showProgressSection =
- unifiedProgress.total > 0 && (task.status === "executing" || task.column === "in-progress");
+ unifiedProgress.total > 0 &&
+ (task.status === "executing" || task.column === "in-progress" || (task.column === "triage" && activeProgressCount > 0));
useEffect(() => {
if (task.column !== "in-progress" && task.column !== "in-review") {
diff --git a/packages/dashboard/app/components/__tests__/TaskCard.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
index ddd044f34e..dcbe64e49b 100644
--- a/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
@@ -2564,6 +2564,79 @@ describe("TaskCard", () => {
expect(container.querySelector(".card-step-name.active")?.textContent).toBe("Step 1");
});
+ it("shows running Plan Review progress while the task is still in triage", () => {
+ const { container } = render(
+ ,
+ );
+
+ expect(screen.getByText("0/2")).toBeDefined();
+ expect(screen.getByText("1 active")).toBeDefined();
+
+ fireEvent.click(screen.getByRole("button", { name: "Show steps" }));
+
+ expect(container.querySelector(".card-step-name.active")?.textContent).toBe("Plan Review");
+ expect(container.querySelector(".card-step-active-badge")?.textContent).toBe("active");
+ const dots = container.querySelectorAll(".card-step-dot");
+ expect(dots[0]?.className).toContain("card-step-dot--running");
+ expect(dots[0]?.className).not.toContain("card-step-dot--pending");
+ });
+
+ it("does not show a false triage active indicator for enabled-but-not-started Plan Review", () => {
+ const { container } = render(
+ ,
+ );
+
+ expect(screen.queryByText("1 active")).toBeNull();
+ expect(container.querySelector(".card-progress")).toBeNull();
+ expect(container.querySelector(".card-steps-toggle")).toBeNull();
+ });
+
+ it("does not render an empty triage progress shell without workflow progress", () => {
+ const { container } = render(
+ ,
+ );
+
+ expect(container.querySelector(".card-progress")).toBeNull();
+ expect(container.querySelector(".card-steps-toggle")).toBeNull();
+ });
+
it("uses singular step label when unified progress total is one", () => {
render(