Files
fusion/packages/engine
gsxdsm 10a0c5848f fix(executor): planner-evacuation lanes come from the emitter — executor leaves the inert list (16 → 12) (#3137)
`executor.ts` was the last file besides `triage.ts` and `scheduler.ts`
on `check-inert-sync-lanes`, holding **4 guards that read as converted
and behave as literals**. Neither cause turned out to be "needs an async
resolver".

## 1. Two of the four were in code with no caller

`isPlannerColumnFor` is a **private method with zero production
callers**. `tsc` reports it unused; the only things reaching it were two
tests casting through `executor as unknown as { … }`, which is exactly
what let it look alive. Its doc comment described the
planning-evacuation branch — but that branch calls
`isBackwardMoveOutOfPlanning` and never called this.

Deleted, along with the two tests whose subject it was. Converting
guards in unreachable code would have "fixed" behaviour that cannot run
and left two more sites to maintain; a test whose subject has no caller
pins nothing.

## 2. The other two no longer need to resolve anything

`isBackwardMoveOutOfPlanning` resolved its own lanes via
`resolvePlannerLanes`, whose selection reader returns `undefined`
unconditionally under PostgreSQL — so it answered with the **default
board for every task**, and both its guards were inert.

Its comment justified the sync resolver by the synchronous `task:moved`
emitter. That was true and **is no longer binding**: the emitter now
resolves lanes once, asynchronously (`moves.ts` →
`resolveWorkflowIrForTask`), and hands them on the payload — which #3112
already reads in this same listener. Reading a parameter is as
synchronous as reading `from`, so nothing reorders and no listener
resolves.

`lanes` is **required, not optional**. An optional parameter that the
one production caller happens to pass is the seam-with-no-supplier shape
this program keeps finding; required means a future caller fails
typecheck instead of silently getting a default board. When the emitter
itself could not resolve, the legacy ids answer — exactly what
`resolvePlannerLanes` degraded to anyway.

## Measured

| | before | after |
|---|---|---|
| `check-inert-sync-lanes` | **16** guards, 3 files | **12** guards, 2
files |
| `executor.ts` on that list | 4 | **0 — off the list** |
| census | 18 | 18 (`--strict`: every file matches baseline exactly) |

**The census is deliberately unchanged.** This targets the inert
population, which the census cannot see by construction: those guards
already read as converted. That gap is the argument in #3082 — 12 guards
still behave as literals while the census shows them as done.

## The producer half, which I nearly shipped without

The predicate's own suite covers it thoroughly — and every case calls it
**directly**. Mutation testing exposed that this proves nothing about
the listener: replacing the listener's `lanes` argument with `undefined`
left `planning-evacuation` at **20/20 green**. That is the fifth failure
shape in this program's learnings verbatim — a converted consumer with
an unconverted producer passing every instrument.

So there is now a case driving the **real listener** on a board whose
planner lanes share no id with the legacy pair (`queued` holds,
`drafting` intakes), withdrawing a card to a non-lifecycle column — the
reported symptom (`todo -> Ideas`) in that board's vocabulary.

## Verification

- engine `tsc` — **0 errors**
- `executor-planner-lanes-resolved` — **12 passed**
- `executor-archive-releases-active-session` — **14 passed**; listener
passing `undefined` → **1 failed | 13 passed**
- `planning-evacuation` + `triage-planning-wake` + archive suite — **47
passed**
- `check-inert-flag-seams`, `check-fnxc-future-dates`, census `--strict`
— exit 0
- `eslint` on changed files — 0 errors

The predicate tests are also **stronger than before**, not merely
adapted: they now build lanes with `toTaskMoveLanes`, the same function
`moves.ts` uses for the payload. Previously they reached the predicate
through the store-backed sync reader, so renamed-lane assertions passed
in the harness while the real path could never see a renamed lane.

## Not done here

The inert baseline still reads 29 against a tree of 12 and the gate
advises re-recording. I left it: a stale allowance is a real hazard, but
re-recording is a one-line change that conflicts with every lane, and it
should land once rather than in each of our branches.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 07:30:43 -07:00
..
2026-07-26 18:11:47 -07:00