Files
fusion/packages/engine
gsxdsm cf7b1a3d46 Drift review (unowned): gridlock detection + autopilot retries resolve the hold column — main 103→101 (#2561)
> **Based on `main`, not on my U7 stack** — merges in any order, no
dependency on #2517.

My assigned files (`triage.ts`, `replan-target.ts`) are at zero, so this
picks up two lifecycle-column literals **no unit's file list claims**.
Both ask *"is this card in the hold column?"* by the id `todo`, and both
are broken **today** for any workflow that renamed it.

## gridlock-detector — the worse of the two

`column !== "todo"` decides which cards count as **schedulable**, and an
empty schedulable set is an **early return**. On a renamed board the
detector concluded *"no gridlock"* at exactly the moment a real one
would be visible.

> A detector that goes quiet on the boards it cannot parse is worse than
one that is absent, because its silence reads as health.

**Converting only the `todo` half would have shipped a still-broken
detector**, and the test caught it. The `active` filter is equally
literal (`in-progress` / `in-review`) — and an empty active set is
*also* an early return. Two literals, one silence.

The `in-progress` half sits **outside the drift review's `todo|triage`
pattern**, which is precisely why a count-driven sweep would have left
it behind and declared the file done. Converted here rather than
deferred as out of scope. Worth flagging to the other workers: the
convergence metric is a good *tracker* but a bad *definition of done* —
an adjacent literal in the same predicate can preserve the whole bug at
a lower score.

## mission-autopilot

The retry compared against `todo` **and moved to the literal `todo`** —
so on a renamed workflow it relocated the card into a column the
workflow may not declare (R7) on **every retry**. Now resolves the hold
role; when the workflow declares none it leaves the card in place and
says so, because the error/status clear still runs, so the retry is not
lost — the card just stays in its own lane.

## Two fixture defects of my own, both caught by the tests failing
wrongly

**My first autopilot tests re-implemented the decision** and asserted on
the copy — proving only that the copy works. That is the anti-pattern
named in
`docs/solutions/store-fake-defects-that-masquerade-as-production-bugs.md`
(#2534) and in the #2527 ratchet review, and I had no excuse: the
constructor takes two stores and `handleTaskFailure` is public.
Rewritten to drive the real method.

**My first gridlock fixture failed on both vocabularies** — the detector
needs three preconditions and I supplied one. A test that fails on its
*no-regression* half is a broken fixture, not a discovered bug. The
"both halves failed" heuristic from that same doc is what flagged it.

That is eight fixture defects across this unit, every one caught by
reading *why* a test failed rather than making it pass.

## Revert proofs, each isolated to one literal

| Restored | Result |
|---|---|
| gridlock hold filter | **1 of 5 fails** (renamed case) |
| autopilot move target | **1 of 5 fails** (renamed case) |

Default-vocabulary halves pass either way — the correct signature for
conversions that change no existing behavior.

## Convergence

Measured against `origin/main` with a comment-stripped scan of `column
=== / !== "todo" | "triage"` in `packages/*/src`, excluding tests:

**103 → 101.**

(The gridlock `active` filter is a third site fixed here that this
pattern does not count.)

## Verification

| Check | Result |
|---|---|
| new suite | 5/5 |
| pre-existing gridlock + autopilot suites | 85/85, **no expectation
edits** |
| `tsc --noEmit` (engine) | clean |
| `pnpm lint` | clean |
| `pnpm test:gate` | green (414 + 10 + 71) |
| `pnpm check:changesets` | clean |

## Still unowned after this

`mission-feature-sync.ts` (1: a planning-lane check) and
`auto-claim-snapshot.ts` (1: `isRunnableAutoClaimCandidate`, a **pure
sync** predicate that needs the injected-lane pattern from #2551, not a
resolve). `notification-service.ts` has one more with a different
semantic — *"has progressed past"* — which needs its own thinking rather
than a mechanical swap. I will take these next unless someone claims
them.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 10:54:35 -07:00
..
2026-07-26 18:11:47 -07:00