fix(engine): backlog pressure called every dependency unfinished on a renamed board (#3081)

## The third lane question

This reporter had **three** lane questions. Two were resolved when the
file's query-blindness was fixed — hold and wip, both through
`resolveProjectColumnsForRoles`. The third sat one method down and was
never touched:

```ts
if (dependency.column !== "done") return false;
```

One board, two lane answers.

## What it cost

On a renamed board every dependency reads unfinished, so
`isRunnableCandidate` rejects every card that has one. The
backlog-pressure alert then names **only dependency-free cards** as the
runnable ones.

The failure mode is the quiet kind: the report still renders, the counts
are right, and the candidate list looks plausible. The operator is told
the queue is blocked on nothing in particular. No default-board test can
see it — which is exactly why the earlier conversion of this same file,
which fixed its reads, left this behind.

## Fix

`finishedColumns` (complete ∪ archived) resolved once by the async
caller alongside hold and wip, then passed into the sync predicate.

- **Required parameter, not optional-with-a-literal-default.** An
optional parameter leaves `done` in the file as a silent fallback and
the next caller gets pre-conversion behaviour by writing nothing.
- **Archived is included** because a dependency that has been archived
is finished too — and this reporter already reads with `includeArchived:
true` precisely so archived blockers resolve.
- **Async resolution.** `resolveProjectColumnsForRoles`' only store read
is `listWorkflowDefinitions()`, a project-wide async read that works
under PostgreSQL. That is the line between a real conversion and the
inert sync-IR kind (#3058), and the new test supplies its board through
that same reader so it exercises the production path.

## Census

| | before | after |
|---|---|---|
| `backlog-pressure-reporter.ts` | 1 | **0** |

## Measured

- One new case; file **11/11 pass**.
- **MUTATION**: restoring `dependency.column !== "done"` fails it.
- The case asserts **both directions in one test** — a dependency
resting in the board's own complete lane makes its card runnable, *and*
a dependency still in the hold lane still blocks it. Asserting only the
first would pass against a predicate that had simply stopped checking
dependencies.
- The file already had a `RENAMED_IR` scoped to its second describe;
mine is a distinct `RENAMED_DEPENDENCY_IR` with different lane names. I
hit the shadowing first and the test failed as `under-threshold` — worth
noting because a same-named fixture that silently resolves to the
*other* board is precisely how a renamed-lane test goes vacuous.
- `tsc --noEmit -p packages/engine` clean; census `--strict`,
`check-lane-wiring`, `check-inert-sync-lane-conversions`,
`check-fnxc-future-dates` clean.

## Flagged, not guessed

Adjacent census entries I looked at and deliberately left:

- **`executor.ts` (4)** — all inside a sync `task:moved` listener.
Converting via `resolveTaskWorkflowIrSync` would be inert for #3058's
reason, and making the listener async reorders it against every other
subscriber. Correctly out of scope, as #3048 judged.
- **`triage.ts:724`** — half-converted in the same shape:
`disposeLanes.hold`/`.intake` come from a sync resolver, so the resolved
arms are themselves inert and "finishing" the guard would add a third
inert comparison.
- **`auto-merge-finalization.ts` (2)** — one is the resolver's
documented degraded fallback (the live arm calls `columnHasFlag`), the
other is already recorded as a deferred signature-widening whose cost
exceeds the error string it sharpens.
- **`in-review-stall.ts:196`** — an explicitly marked DELIBERATE-LITERAL
no-metadata fallback.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-31 03:51:43 -07:00
committed by GitHub
parent 3c531d984c
commit 9e242ea294
3 changed files with 106 additions and 8 deletions

View File

@@ -19,7 +19,6 @@
"packages/core/src/task-store/task-id-integrity.ts": 1,
"packages/dashboard/app/components/ResearchTaskActionModal.tsx": 1,
"packages/dashboard/app/components/TaskCard.tsx": 1,
"packages/engine/src/backlog-pressure-reporter.ts": 1,
"packages/engine/src/triage.ts": 1
},
"deliberateByFile": {