feat(FN-3977): complete Step 3 — overlap bottleneck dashboard copy

Fusion-Task-Id: FN-3977
Fusion-Task-Lineage: c84f2ce0-3726-49b2-ad84-6703edbfecf1
This commit is contained in:
Fusion
2026-05-14 15:41:23 -07:00
committed by gsxdsm
parent eb9a838d12
commit e5b5d5c9e5
6 changed files with 74 additions and 36 deletions

View File

@@ -94,18 +94,19 @@ export function ExecutorStatusBar({ tasks, projectId, taskStuckTimeoutMs, staleH
const relativeTime = useMemo(() => formatRelativeTime(stats.lastActivityAt), [stats.lastActivityAt]);
const highestEscalatedBlocker = useMemo(() => {
const highestOverlapBlocker = useMemo(() => {
const fanoutMap = computeBlockerFanoutMap(tasks, {
staleHighFanoutAgeThresholdMs:
staleHighFanoutBlockerAgeThresholdMs ?? STALE_HIGH_FANOUT_BLOCKER_AGE_THRESHOLD_MS,
});
const candidates = Array.from(fanoutMap.values())
.map((entry) => entry.escalation)
.filter((entry): entry is NonNullable<typeof entry> => Boolean(entry))
const candidates = Array.from(fanoutMap.entries())
.map(([blockerId, entry]) => ({ blockerId, entry }))
.filter(({ entry }) => entry.isHighFanout)
.sort((a, b) => {
if (b.activeTodoCount !== a.activeTodoCount) return b.activeTodoCount - a.activeTodoCount;
if (b.totalActiveCount !== a.totalActiveCount) return b.totalActiveCount - a.totalActiveCount;
if (b.blockingAgeMs !== a.blockingAgeMs) return b.blockingAgeMs - a.blockingAgeMs;
if (b.entry.overlapBlockedTodoCount !== a.entry.overlapBlockedTodoCount) return b.entry.overlapBlockedTodoCount - a.entry.overlapBlockedTodoCount;
const aAge = a.entry.escalation?.blockingAgeMs ?? 0;
const bAge = b.entry.escalation?.blockingAgeMs ?? 0;
if (bAge !== aAge) return bAge - aAge;
return a.blockerId.localeCompare(b.blockerId, "en", { numeric: true, sensitivity: "base" });
});
@@ -212,17 +213,17 @@ export function ExecutorStatusBar({ tasks, projectId, taskStuckTimeoutMs, staleH
<span className="executor-status-bar__count">{stats.inReviewCount}</span>
</div>
{highestEscalatedBlocker && (
{highestOverlapBlocker && (
<>
<span className="executor-status-bar__divider" aria-hidden="true" />
<div className="executor-status-bar__segment executor-status-bar__segment--fanout">
<span className="executor-status-bar__indicator executor-status-bar__indicator--fanout executor-status-bar__indicator--active" aria-hidden="true" />
<span className="executor-status-bar__label">Escalated</span>
<span className="executor-status-bar__label">Overlap queue</span>
<span
className="executor-status-bar__fanout-summary"
title={`Escalated blocker ${highestEscalatedBlocker.blockerId}: ${highestEscalatedBlocker.activeTodoCount} todo waiting (threshold ${HIGH_FANOUT_BLOCKER_TODO_THRESHOLD}), ${highestEscalatedBlocker.totalActiveCount} active total`}
title={`${highestOverlapBlocker.entry.escalation ? "Escalated" : "Temporary"} overlap bottleneck ${highestOverlapBlocker.blockerId}: ${highestOverlapBlocker.entry.overlapBlockedTodoCount} todo blocked via blockedBy (threshold ${HIGH_FANOUT_BLOCKER_TODO_THRESHOLD})`}
>
{highestEscalatedBlocker.blockerId} · {highestEscalatedBlocker.activeTodoCount} todo
{highestOverlapBlocker.blockerId} · {highestOverlapBlocker.entry.overlapBlockedTodoCount} todo{highestOverlapBlocker.entry.escalation ? " (escalated)" : ""}
</span>
</div>
</>

View File

@@ -432,6 +432,7 @@ function areTaskCardPropsEqual(previous: TaskCardProps, next: TaskCardProps): bo
previous.fanout?.totalCount === next.fanout?.totalCount &&
previous.fanout?.activeTodoCount === next.fanout?.activeTodoCount &&
previous.fanout?.isHighFanout === next.fanout?.isHighFanout &&
previous.fanout?.overlapBlockedTodoCount === next.fanout?.overlapBlockedTodoCount &&
previous.fanout?.escalation?.blockingAgeMs === next.fanout?.escalation?.blockingAgeMs &&
areTaskDependenciesEqual(previous.fanout?.dependentIds ?? [], next.fanout?.dependentIds ?? []) &&
areTaskDependenciesEqual(previous.fanout?.staleBlockedByDependentIds ?? [], next.fanout?.staleBlockedByDependentIds ?? []) &&
@@ -1793,11 +1794,11 @@ function TaskCardComponent({
{fanout && fanout.totalCount > 0 && (
<span
className={`card-fanout-badge${fanout.staleBlockedByDependentIds.length > 0 ? " card-fanout-badge--stale" : ""}`}
data-tooltip={`Blocking ${fanout.totalCount} active task(s); ${fanout.activeTodoCount} waiting in todo${fanout.isHighFanout ? ` (high fan-out threshold: ${HIGH_FANOUT_BLOCKER_TODO_THRESHOLD})` : ""}${fanout.escalation ? ` · escalated after ${Math.floor(fanout.escalation.blockingAgeMs / 60000)}m in blocking column` : ""}`}
data-tooltip={`Blocking ${fanout.totalCount} active task(s); overlap blockedBy queue: ${fanout.overlapBlockedTodoCount} todo${fanout.isHighFanout ? ` (overlap bottleneck threshold: ${HIGH_FANOUT_BLOCKER_TODO_THRESHOLD})` : ""}${fanout.escalation ? ` · escalated after ${Math.floor(fanout.escalation.blockingAgeMs / 60000)}m in blocking column` : ""}`}
>
<GitBranch size={12} style={{ verticalAlign: "middle" }} />
<span>
{fanout.escalation ? "Escalated" : fanout.isHighFanout ? "High fan-out" : "Blocks"}{" "}
{fanout.escalation ? "Escalated overlap" : fanout.isHighFanout ? "Overlap bottleneck" : "Blocks"}{" "}
<span className="card-fanout-count">{fanout.totalCount}</span>
{fanout.staleBlockedByDependentIds.length > 0 ? ` (${fanout.staleBlockedByDependentIds.length} stale)` : ""}
</span>

View File

@@ -1894,6 +1894,10 @@ export function TaskDetailContent({
});
}, [blockingEntry, tasks]);
const overlapBlockingSummary = blockingEntry
? `${task.id} is blocking ${blockingEntry.overlapBlockedTodoCount} todo task(s) via blockedBy overlap`
: null;
const assignedAgentLabel = assignedAgent?.name ?? task.assignedAgentId ?? null;
const detailProviders = useMemo(() => {
const providers: string[] = [];
@@ -3094,6 +3098,11 @@ export function TaskDetailContent({
</div>
<div className="detail-deps detail-blocking">
<h4>Blocking</h4>
{blockingEntry && (
<div className="detail-empty-inline">
{overlapBlockingSummary}
</div>
)}
{blockingDependents.length > 0 ? (
<ul className="detail-dep-list">
{blockingDependents.map((dependent) => (

View File

@@ -69,20 +69,20 @@ describe("ExecutorStatusBar", () => {
expect(statusBar).not.toHaveTextContent("Escalated");
});
it("shows highest escalated blocker summary with stable tie-break ordering", () => {
it("shows overlap bottleneck summary with stable tie-break ordering", () => {
const tasks = [
makeTask("FN-010", "in-progress", { columnMovedAt: "2026-01-01T00:00:00.000Z", updatedAt: "2026-01-01T00:00:00.000Z" }),
makeTask("FN-002", "in-review", { columnMovedAt: "2026-01-01T00:00:00.000Z", updatedAt: "2026-01-01T00:00:00.000Z" }),
makeTask("FN-101", "todo", { dependencies: ["FN-010"] }),
makeTask("FN-102", "todo", { dependencies: ["FN-010"] }),
makeTask("FN-103", "todo", { dependencies: ["FN-010"] }),
makeTask("FN-104", "todo", { dependencies: ["FN-010"] }),
makeTask("FN-105", "todo", { dependencies: ["FN-010"] }),
makeTask("FN-201", "todo", { dependencies: ["FN-002"] }),
makeTask("FN-202", "todo", { dependencies: ["FN-002"] }),
makeTask("FN-203", "todo", { dependencies: ["FN-002"] }),
makeTask("FN-204", "todo", { dependencies: ["FN-002"] }),
makeTask("FN-205", "todo", { dependencies: ["FN-002"] }),
makeTask("FN-101", "todo", { blockedBy: "FN-010" }),
makeTask("FN-102", "todo", { blockedBy: "FN-010" }),
makeTask("FN-103", "todo", { blockedBy: "FN-010" }),
makeTask("FN-104", "todo", { blockedBy: "FN-010" }),
makeTask("FN-105", "todo", { blockedBy: "FN-010" }),
makeTask("FN-201", "todo", { blockedBy: "FN-002" }),
makeTask("FN-202", "todo", { blockedBy: "FN-002" }),
makeTask("FN-203", "todo", { blockedBy: "FN-002" }),
makeTask("FN-204", "todo", { blockedBy: "FN-002" }),
makeTask("FN-205", "todo", { blockedBy: "FN-002" }),
];
render(
@@ -93,11 +93,11 @@ describe("ExecutorStatusBar", () => {
);
const statusBar = screen.getByRole("status");
expect(statusBar).toHaveTextContent("Escalated");
expect(statusBar).toHaveTextContent("Overlap queue");
expect(statusBar).toHaveTextContent("FN-002 · 5 todo");
});
it("does not show escalated summary for ordinary chains below threshold", () => {
it("does not show overlap queue summary for ordinary chains below threshold", () => {
const tasks = [
makeTask("FN-500", "in-progress"),
makeTask("FN-501", "todo", { dependencies: ["FN-500"] }),

View File

@@ -99,6 +99,10 @@ const highFanout = {
totalCount: 7,
activeTodoCount: 3,
dependentIds: ["FN-002", "FN-003"],
dependencyDependentIds: [],
overlapBlockedDependentIds: ["FN-002", "FN-003"],
overlapBlockedActiveCount: 3,
overlapBlockedTodoCount: 3,
staleBlockedByDependentIds: [],
isHighFanout: true,
} as const;
@@ -598,7 +602,7 @@ describe("TaskCard", () => {
rerender(
<TaskCard
task={makeTask({ column: "todo" })}
fanout={{ totalCount: 0, activeTodoCount: 0, dependentIds: [], staleBlockedByDependentIds: [], isHighFanout: false }}
fanout={{ totalCount: 0, activeTodoCount: 0, dependentIds: [], dependencyDependentIds: [], overlapBlockedDependentIds: [], overlapBlockedActiveCount: 0, overlapBlockedTodoCount: 0, staleBlockedByDependentIds: [], isHighFanout: false }}
onOpenDetail={noop}
addToast={noop}
/>,
@@ -611,7 +615,7 @@ describe("TaskCard", () => {
render(
<TaskCard
task={makeTask({ column: "in-progress" })}
fanout={{ totalCount: 7, activeTodoCount: 4, dependentIds: ["FN-002"], staleBlockedByDependentIds: [], isHighFanout: false }}
fanout={{ totalCount: 7, activeTodoCount: 4, dependentIds: ["FN-002"], dependencyDependentIds: ["FN-002"], overlapBlockedDependentIds: [], overlapBlockedActiveCount: 0, overlapBlockedTodoCount: 0, staleBlockedByDependentIds: [], isHighFanout: false }}
onOpenDetail={noop}
addToast={noop}
/>,
@@ -620,14 +624,14 @@ describe("TaskCard", () => {
const badge = screen.getByText("Blocks").closest(".card-fanout-badge") as HTMLElement;
expect(badge).not.toBeNull();
expect(badge.textContent).toContain("Blocks 7");
expect(badge.getAttribute("data-tooltip")).toBe("Blocking 7 active task(s); 4 waiting in todo");
expect(badge.getAttribute("data-tooltip")).toContain("overlap blockedBy queue: 0 todo");
});
it("applies stale fan-out modifier when stale blockedBy dependents exist", () => {
const { container } = render(
<TaskCard
task={makeTask({ column: "in-progress" })}
fanout={{ totalCount: 3, activeTodoCount: 1, dependentIds: ["FN-003"], staleBlockedByDependentIds: ["FN-003"], isHighFanout: false }}
fanout={{ totalCount: 3, activeTodoCount: 1, dependentIds: ["FN-003"], dependencyDependentIds: [], overlapBlockedDependentIds: ["FN-003"], overlapBlockedActiveCount: 1, overlapBlockedTodoCount: 1, staleBlockedByDependentIds: ["FN-003"], isHighFanout: false }}
onOpenDetail={noop}
addToast={noop}
/>,
@@ -638,7 +642,7 @@ describe("TaskCard", () => {
expect(badge.textContent).toContain("(1 stale)");
});
it("renders high fan-out badge without visible todo suffix while keeping tooltip context", () => {
it("renders overlap bottleneck badge without visible todo suffix while keeping tooltip context", () => {
render(
<TaskCard
task={makeTask({ column: "in-progress" })}
@@ -648,11 +652,11 @@ describe("TaskCard", () => {
/>,
);
const badge = screen.getByText("High fan-out").closest(".card-fanout-badge") as HTMLElement;
const badge = screen.getByText("Overlap bottleneck").closest(".card-fanout-badge") as HTMLElement;
expect(badge).not.toBeNull();
expect(badge.textContent).toContain("High fan-out 7");
expect(badge.textContent).toContain("Overlap bottleneck 7");
expect(badge.textContent).not.toContain("todo)");
expect(badge.getAttribute("data-tooltip")).toContain("3 waiting in todo");
expect(badge.getAttribute("data-tooltip")).toContain("overlap blockedBy queue: 3 todo");
});
it("escalates only threshold-crossing fan-out badges", () => {
@@ -663,6 +667,8 @@ describe("TaskCard", () => {
...highFanout,
totalCount: 8,
activeTodoCount: 5,
overlapBlockedTodoCount: 5,
overlapBlockedActiveCount: 8,
dependentIds: ["FN-003"],
escalation: { blockerId: "FN-001", activeTodoCount: 5, totalActiveCount: 8, blockingAgeMs: 3_600_000 },
}}
@@ -671,7 +677,7 @@ describe("TaskCard", () => {
/>,
);
let badge = screen.getByText("Escalated").closest(".card-fanout-badge") as HTMLElement;
let badge = screen.getByText("Escalated overlap").closest(".card-fanout-badge") as HTMLElement;
expect(badge).not.toBeNull();
expect(badge.textContent).toContain("Escalated");
expect(badge.textContent).toContain("8");
@@ -680,7 +686,7 @@ describe("TaskCard", () => {
rerender(
<TaskCard
task={makeTask({ column: "in-progress" })}
fanout={{ totalCount: 8, activeTodoCount: 4, dependentIds: ["FN-003"], staleBlockedByDependentIds: [], isHighFanout: false }}
fanout={{ totalCount: 8, activeTodoCount: 4, dependentIds: ["FN-003"], dependencyDependentIds: ["FN-003"], overlapBlockedDependentIds: [], overlapBlockedActiveCount: 0, overlapBlockedTodoCount: 0, staleBlockedByDependentIds: [], isHighFanout: false }}
onOpenDetail={noop}
addToast={noop}
/>,

View File

@@ -312,6 +312,27 @@ describe("TaskDetailModal", () => {
});
});
it("shows overlap blockedBy summary in Blocking section", () => {
render(
<TaskDetailModal
task={makeTask({ id: "FN-B", column: "in-progress" })}
tasks={[
makeTask({ id: "FN-B", column: "in-progress" }),
makeTask({ id: "FN-1", column: "todo", blockedBy: "FN-B" }),
makeTask({ id: "FN-2", column: "todo", blockedBy: "FN-B" }),
]}
onClose={noop}
onMoveTask={noopMove}
onDeleteTask={noopDelete}
onMergeTask={noopMerge}
onOpenDetail={noopOpenDetail}
addToast={noop}
/>,
);
expect(screen.getByText("FN-B is blocking 2 todo task(s) via blockedBy overlap")).toBeInTheDocument();
});
it("renders modal wrapper structure and default close control", () => {
const { container } = render(
<TaskDetailModal