Files
fusion/plugins/fusion-plugin-dependency-graph/src/__tests__
gsxdsm cadf011b25 fix(dependency-graph): an allowlist of four legacy lanes rendered a blank graph on a renamed board (#2972)
## A renamed board gets a blank dependency graph

`filterGraphTasks` gated on an allowlist of four legacy lane ids:

```ts
export const INCLUDED_COLUMNS = new Set(["triage", "todo", "in-progress", "in-review"]);
export function filterGraphTasks(tasks: Task[]): Task[] {
  return tasks.filter((task) => INCLUDED_COLUMNS.has(task.column));
}
```

On a board whose lanes are named anything else — `backlog`, `building`,
`checking` — **no card matches and the graph renders completely empty**.
This is not a mislabelled node or a missing edge: the entire feature is
blank, and it reads as *"this project has no dependencies"* rather than
as a bug. `triage` is in that allowlist too, a lane U11 (#2515) deleted.

## Fix: gate on the finished lanes instead

Inverted to a denylist, so the **default is the safe one**. An
unrecognised lane is active work by assumption and renders; only lanes
that genuinely mean "finished" drop out.

An allowlist fails **closed** — hide everything unknown. A denylist
fails **open** — show it. For a graph, an extra node is a far smaller
error than no graph.

## The residual, named rather than hidden

`EXCLUDED_COLUMNS` is still two literals. `DependencyGraph.tsx` is a
client React component handed plain `Task` rows as a prop, with no async
seam to resolve a workflow IR — so a board that renames its DONE lane
still shows finished cards here. That is deliberately the mild failure
direction: "one extra node", not "no graph". It is documented in the
code rather than papered over with an optional resolved-lanes parameter
no caller could fill.

The invalid-column guard is now **explicit**. Under the allowlist,
`column: undefined` was excluded as a side effect of not being in the
set; under a denylist it would sail through, so it is checked directly
and covered.

## Revert proof

Restoring only `filters.ts`:

```
AssertionError: expected [] to deeply equal [ 'FN-1', 'FN-2', 'FN-3' ]
AssertionError: expected [] to deeply equal [ 'FN-1' ]
      Tests  2 failed | 13 passed (15)
```

Worth noting explicitly: **every pre-existing case passes either way.**
They only ever enumerate the six legacy ids, so the allowlist and the
denylist agree on all of them — no existing test could have seen this
blackout. (The new empty-string case also passes both ways; it guards
the new implementation rather than proving the fix, and I am not
claiming it as coverage of the defect.)

## Verification (measured)

- plugin suite — **183 passed / 20 files**
- `tsc --noEmit`, `eslint` — clean
- `lifecycle-column-census --strict`, `check-fnxc-future-dates` — green

## Checked and deliberately not changed

`GraphTaskNode.tsx` holds `column === "in-progress"` and `column ===
"in-review"`. Both are already audited with an in-code note, and both
degrade mildly rather than blanking anything — `hasExecutionSignal` also
ORs on `ACTIVE_STATUSES`, so a renamed WIP lane still reads as active
via status. Converting them needs column traits this component is not
given, so they stay noted rather than half-converted.

No changeset: `fusion-plugin-dependency-graph` is `private: true`.
2026-07-30 22:22:28 -07:00
..