fix: drop dependency-graph plugin's use of removed stuck-task tagging
The dependency-graph plugin imported the deleted taskStuck util through its dashboard interop shim, breaking the dashboard vite build. Remove the isStuck gate from graph nodes, the taskStuckTimeoutMs prop threading, the taskStuck module declaration from dashboard-interop.d.ts, and the stalled-card-as-stuck test coverage. Covered by the existing remove-stuck-task-tagging changeset. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,6 @@ export interface DependencyGraphProps {
|
||||
onDeleteTask?: (id: string, options?: { removeDependencyReferences?: boolean }) => Promise<Task>;
|
||||
onRetryTask?: (id: string) => Promise<Task>;
|
||||
onOpenDetailWithTab?: (task: Task, initialTab: "changes") => void;
|
||||
taskStuckTimeoutMs?: number;
|
||||
onOpenMission?: (missionId: string) => void;
|
||||
onMoveTask?: (id: string, column: Task["column"], optionsOrPosition?: { preserveProgress?: boolean } | number) => Promise<Task>;
|
||||
lastFetchTimeMs?: number;
|
||||
@@ -53,7 +52,6 @@ export function DependencyGraph({
|
||||
onDeleteTask,
|
||||
onRetryTask,
|
||||
onOpenDetailWithTab,
|
||||
taskStuckTimeoutMs,
|
||||
onOpenMission,
|
||||
onMoveTask,
|
||||
lastFetchTimeMs,
|
||||
@@ -500,7 +498,6 @@ export function DependencyGraph({
|
||||
onDeleteTask={onDeleteTask}
|
||||
onRetryTask={onRetryTask}
|
||||
onOpenDetailWithTab={onOpenDetailWithTab}
|
||||
taskStuckTimeoutMs={taskStuckTimeoutMs}
|
||||
onOpenMission={onOpenMission}
|
||||
onMoveTask={onMoveTask}
|
||||
lastFetchTimeMs={lastFetchTimeMs}
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { TraitFlags } from "@fusion/core";
|
||||
import type { GraphPosition } from "./types.js";
|
||||
import { useNodeDrag } from "./hooks/useNodeDrag.js";
|
||||
import { TaskCard } from "@fusion/dashboard/app/components/TaskCard";
|
||||
import { isTaskStuck } from "@fusion/dashboard/app/utils/taskStuck";
|
||||
import "./GraphTaskNode.css";
|
||||
import "./GraphHighlight.css";
|
||||
import "./styles/drag.css";
|
||||
@@ -23,7 +22,6 @@ type TaskCardBridgeProps = Pick<
|
||||
| "onDeleteTask"
|
||||
| "onRetryTask"
|
||||
| "onOpenDetailWithTab"
|
||||
| "taskStuckTimeoutMs"
|
||||
| "onOpenMission"
|
||||
| "onMoveTask"
|
||||
| "lastFetchTimeMs"
|
||||
@@ -34,10 +32,8 @@ export interface GraphTaskNodeProps extends TaskCardBridgeProps, Pick<HTMLAttrib
|
||||
FNXC:WorkflowLifecycleColumns 2026-07-31-15:30:
|
||||
This card's OWN resolved column traits, threaded from the host's plugin view context.
|
||||
|
||||
Two defects close here, both from this component having no access to the board's vocabulary:
|
||||
- `isTaskStuck` was called without its `columnFlags` argument, so `isWipColumnRole` fell back to
|
||||
the literal and NO card in the graph was ever shown stuck on a renamed board — while the same
|
||||
card showed stuck correctly on the main board. That asymmetry was the tell.
|
||||
Defect closed here, from this component having no access to the board's vocabulary
|
||||
(a second consumer, isTaskStuck, was deleted with the stuck-tag removal):
|
||||
- The `TaskCard` rendered below is a THIRD producer of unflagged cards, after the two #3025 fixed.
|
||||
It bypasses `renderTaskCard` entirely by importing the component directly, so a host-side fix
|
||||
could not reach it; every role helper inside it read the legacy ids.
|
||||
@@ -83,10 +79,10 @@ export function GraphTaskNode({
|
||||
onNodeDragEnd,
|
||||
...taskCardProps
|
||||
}: GraphTaskNodeProps) {
|
||||
const { task, globalPaused, taskStuckTimeoutMs, lastFetchTimeMs, onOpenDetail } = taskCardProps;
|
||||
const { task, globalPaused, onOpenDetail } = taskCardProps;
|
||||
const isFailed = task.status === "failed";
|
||||
const isPaused = task.paused === true;
|
||||
const isStuck = isTaskStuck(task, taskStuckTimeoutMs, lastFetchTimeMs, taskColumnFlags);
|
||||
// FNXC:StuckTagRemoval 2026-08-17-22:30: stuck-task tagging was removed from the dashboard; graph nodes no longer derive or suppress on a stuck state.
|
||||
/*
|
||||
FNXC:PluginLifecycleColumns 2026-07-30-03:40 (U11 #2515 audit):
|
||||
Keyed on `column === "triage"`, this went permanently FALSE for default-lineage
|
||||
@@ -122,7 +118,6 @@ export function GraphTaskNode({
|
||||
!globalPaused &&
|
||||
!isFailed &&
|
||||
!isPaused &&
|
||||
!isStuck &&
|
||||
!isAwaitingApproval &&
|
||||
hasExecutionSignal;
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ function props(overrides: Partial<React.ComponentProps<typeof GraphTaskNode>> =
|
||||
onOpenDetailWithTab: vi.fn(),
|
||||
onMoveTask: vi.fn(),
|
||||
onOpenMission: vi.fn(),
|
||||
taskStuckTimeoutMs: 1000,
|
||||
lastFetchTimeMs: Date.now(),
|
||||
workflowStepNameLookup: new Map<string, string>(),
|
||||
...overrides,
|
||||
|
||||
@@ -48,7 +48,6 @@ function createProps(task: Task) {
|
||||
onOpenDetailWithTab: vi.fn(),
|
||||
onMoveTask: vi.fn(),
|
||||
onOpenMission: vi.fn(),
|
||||
taskStuckTimeoutMs: 60_000,
|
||||
lastFetchTimeMs: Date.now(),
|
||||
workflowStepNameLookup: new Map<string, string>(),
|
||||
};
|
||||
@@ -384,38 +383,8 @@ describe("GraphTaskNode", () => {
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
FNXC:WorkflowLifecycleColumns 2026-07-31-15:30:
|
||||
THE INVARIANT: a stalled card in the board's OWN wip lane reads as stuck, not as healthily running.
|
||||
|
||||
`isTaskStuck` was called without its `columnFlags` argument, so `isWipColumnRole` fell back to the
|
||||
literal `in-progress`. On a renamed board no graph card could ever be stuck — and because `isStuck`
|
||||
gates `isActive`, a wedged card rendered with the ACTIVE styling instead: the graph said "running"
|
||||
about a task that had not moved in hours, while the main board showed the same card as stuck.
|
||||
|
||||
That asymmetry between two views of one task is the whole defect, and it is what this pins.
|
||||
|
||||
Reverted (the 4th argument dropped, or the flags not threaded from the host context), the first case
|
||||
gets the `--active` class back and fails.
|
||||
*/
|
||||
describe("stuck detection on a renamed board", () => {
|
||||
const STALE_MS = 120_000;
|
||||
|
||||
function stalledCard() {
|
||||
return createTask({
|
||||
column: "building",
|
||||
status: "executing",
|
||||
updatedAt: new Date(Date.now() - STALE_MS).toISOString(),
|
||||
} as Partial<Task>);
|
||||
}
|
||||
|
||||
it("treats a stalled card in a RENAMED wip lane as stuck, not active", () => {
|
||||
const props = createProps(stalledCard());
|
||||
render(<GraphTaskNode {...props} taskColumnFlags={{ countsTowardWip: true }} />);
|
||||
|
||||
expect(screen.getByTestId("graph-task-node-FN-TEST").className).not.toContain("graph-task-node--active");
|
||||
});
|
||||
|
||||
// FNXC:StuckTagRemoval 2026-08-17-22:30: stuck-task tagging removed from the dashboard; the stalled-card-as-stuck coverage went with it.
|
||||
describe("active styling", () => {
|
||||
it("still reads a legacy in-progress card as active when it is fresh", () => {
|
||||
const props = createProps(createTask({ column: "in-progress", status: "executing", updatedAt: new Date().toISOString() } as Partial<Task>));
|
||||
render(<GraphTaskNode {...props} />);
|
||||
|
||||
@@ -157,7 +157,6 @@ describe("dependency graph interactions", () => {
|
||||
onOpenDetailWithTab={vi.fn()}
|
||||
onMoveTask={vi.fn()}
|
||||
onOpenMission={vi.fn()}
|
||||
taskStuckTimeoutMs={1_000}
|
||||
lastFetchTimeMs={Date.now()}
|
||||
workflowStepNameLookup={new Map()}
|
||||
/>,
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
declare module "@fusion/dashboard/app/utils/taskStuck" {
|
||||
import type { Task, TraitFlags } from "@fusion/core";
|
||||
|
||||
/* FNXC:WorkflowLifecycleColumns 2026-07-31-15:30: the 4th parameter existed upstream and this shim
|
||||
did not declare it, so the plugin could not pass resolved traits even once it had them — and
|
||||
`isWipColumnRole` fell back to the literal, meaning NO card in the graph was ever shown stuck on a
|
||||
renamed board while the same card showed stuck correctly on the main board. */
|
||||
export function isTaskStuck(
|
||||
task: Task,
|
||||
/* FNXC:PluginInteropDrift 2026-07-31-07:50: positionally REQUIRED in the real signature
|
||||
(`number | undefined`), not optional — a mirror that is merely approximate is the drift this
|
||||
file already caused once. Found by check-plugin-interop-drift. */
|
||||
taskStuckTimeoutMs: number | undefined,
|
||||
lastFetchTimeMs?: number,
|
||||
columnFlags?: Partial<TraitFlags>,
|
||||
): boolean;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:StuckTagRemoval 2026-08-17-22:30:
|
||||
The taskStuck shim was deleted with the dashboard's stuck-task tagging; the host module no longer exists.
|
||||
*/
|
||||
declare module "@fusion/dashboard/app/plugins/types" {
|
||||
import type { ReactNode } from "react";
|
||||
import type { Task, TaskDetail, TraitFlags, WorkflowStep } from "@fusion/core";
|
||||
@@ -57,7 +43,6 @@ declare module "@fusion/dashboard/app/components/TaskCard" {
|
||||
onDeleteTask?: (id: string, options?: { removeDependencyReferences?: boolean }) => Promise<Task>;
|
||||
onRetryTask?: (id: string) => Promise<Task>;
|
||||
onOpenDetailWithTab?: (task: Task | TaskDetail, initialTab: "changes") => void;
|
||||
taskStuckTimeoutMs?: number;
|
||||
onOpenMission?: (missionId: string) => void;
|
||||
onMoveTask?: (id: string, column: Column, optionsOrPosition?: { preserveProgress?: boolean } | number) => Promise<Task>;
|
||||
lastFetchTimeMs?: number;
|
||||
|
||||
Reference in New Issue
Block a user