fix(dashboard): count visibly active cards (REVISING replans) in column header processing count
A Todo card parked in the durable needs-replan stage keeps its REVISING badge and activity chrome (FN-8494), but the header only counted live agents, so it read 0/2 under a glowing card. Union the shared Running predicate with the card's own activity predicate (same globalPaused/stuck gates) so the header equals the number of visibly active cards. Footer Running and admission keep live-agent-only truth: a parked replan must not consume concurrency capacity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
7
.changeset/board-header-active-chrome-count.md
Normal file
7
.changeset/board-header-active-chrome-count.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Board column headers now count REVISING (replan) cards and other visibly active cards in the processing count.
|
||||
category: fix
|
||||
dev: Column header count = shared Running predicate ∪ card activity-chrome predicate (isTaskAgentActive); footer/admission keep live-agent-only semantics.
|
||||
@@ -11,6 +11,8 @@ import { WorktreeGroup } from "./WorktreeGroup";
|
||||
import { QuickEntryBox } from "./QuickEntryBox";
|
||||
import { PluginSlot } from "./PluginSlot";
|
||||
import { groupByWorktree } from "../utils/worktreeGrouping";
|
||||
import { isTaskAgentActive } from "../utils/taskActivity";
|
||||
import { isTaskStuck } from "../utils/taskStuck";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
import type { TaskContextMenuColumnMetadata } from "./TaskContextMenu";
|
||||
import { ChevronDown, ChevronUp, MoreVertical } from "lucide-react";
|
||||
@@ -291,10 +293,22 @@ function ColumnComponent({ column, tasks, projectId, maxConcurrent, showWorktree
|
||||
FNXC:BoardColumnCount 2026-07-21-19:30:
|
||||
Column header is executing/total (e.g. 3/4). Executing uses the same Running predicate as the
|
||||
footer (unpaused WIP, live planners, active review). Total is the card count in this lane.
|
||||
|
||||
FNXC:BoardColumnCount 2026-07-22-06:10:
|
||||
The header must agree with the cards below it: a Todo card parked in the durable
|
||||
`needs-replan` stage keeps its REVISING badge and activity chrome (FN-8494), so a header
|
||||
that only counts live agents read 0/2 under a glowing card. Union the shared Running
|
||||
predicate with the card's own activity-chrome predicate (isTaskAgentActive, same
|
||||
globalPaused/stuck gates the card applies) so the count equals the number of visibly
|
||||
active cards. Footer Running and admission intentionally keep the live-agent-only truth —
|
||||
a parked replan must not consume top-level concurrency capacity.
|
||||
*/
|
||||
const activeTaskCount = useMemo(
|
||||
() => tasks.filter((task) => isRunningAgentTask(enrichRunningAgentTaskShapeFromFlags(task, columnFlags))).length,
|
||||
[tasks, columnFlags],
|
||||
() => tasks.filter((task) =>
|
||||
isRunningAgentTask(enrichRunningAgentTaskShapeFromFlags(task, columnFlags))
|
||||
|| isTaskAgentActive(task, { globalPaused, isStuck: isTaskStuck(task, taskStuckTimeoutMs, lastFetchTimeMs) }),
|
||||
).length,
|
||||
[tasks, columnFlags, globalPaused, taskStuckTimeoutMs, lastFetchTimeMs],
|
||||
);
|
||||
// When search is active, skip pagination so all matching tasks are visible
|
||||
const shouldPaginate = !isArchived && !isSearchActive && !showWorktreeGroups && tasks.length > PAGINATED_COLUMN_THRESHOLD;
|
||||
|
||||
@@ -179,6 +179,44 @@ describe("Column count-flash", () => {
|
||||
|
||||
expect(screen.getByLabelText("1 executing of 4")).toHaveTextContent("1/4");
|
||||
});
|
||||
|
||||
it("counts cards with active chrome — a REVISING (needs-replan) todo card and a live code-review gate", () => {
|
||||
// Header must equal the number of visibly active cards (FN-8494 keeps REVISING chrome on
|
||||
// parked replans; a live gate session runs with null status and a pending step lease).
|
||||
const tasks = [
|
||||
{ ...makeTask("FN-001"), column: "todo" as ColumnType, status: "needs-replan" as any },
|
||||
{ ...makeTask("FN-002"), column: "todo" as ColumnType },
|
||||
];
|
||||
render(
|
||||
<Column
|
||||
{...defaultProps}
|
||||
column={"todo" as ColumnType}
|
||||
columnFlags={{ hold: true }}
|
||||
tasks={tasks}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByLabelText("1 executing of 2")).toHaveTextContent("1/2");
|
||||
});
|
||||
|
||||
it("counts an in-review card whose code-review gate holds a pending step lease", () => {
|
||||
const tasks = [
|
||||
{
|
||||
...makeTask("FN-001"),
|
||||
column: "in-review" as ColumnType,
|
||||
workflowStepResults: [{ workflowStepId: "code-review", workflowStepName: "Code Review", status: "pending" as const, startedAt: new Date().toISOString() }],
|
||||
},
|
||||
{ ...makeTask("FN-002"), column: "in-review" as ColumnType },
|
||||
];
|
||||
render(
|
||||
<Column
|
||||
{...defaultProps}
|
||||
column={"in-review" as ColumnType}
|
||||
columnFlags={{ mergeBlocker: true }}
|
||||
tasks={tasks}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByLabelText("1 executing of 2")).toHaveTextContent("1/2");
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user