Files
fusion/scripts
gsxdsm 6483f9ce2b fix(scheduler): resolve task:updated / task:deleted lanes asynchronously (scheduler inert 5 → 0) (#3128)
The last inert guards in `scheduler.ts`. Independent of my other
branches.

## Inert-guard ratchet

| Scope | Before | After |
|---|---:|---:|
| `scheduler.ts` | 5 | **0** |
| total | 12 | **7** (triage.ts 8 → other worker; executor.ts 4 → #3112)
|

## The live bug

These read `resolveTaskParkedColumnsSync`, which answers with the
**default** workflow in production. On a renamed board the scheduler
**never woke** on unpause or planning-finish, and a **deleted blocker
never unblocked its dependents** — the card sat behind a task that no
longer existed.

## The criterion, restated because I got it wrong before

**What blocks a guard is whether its answer is consumed synchronously —
not whether the enclosing listener is declared sync.** I assumed the
latter earlier in this program and reverted for it.

All three fail that test: two only gate `schedule()`, which is itself
`async`, fire-and-forget and re-entrance-guarded; the third already sits
below an `await getSettings()`. The edge-trigger bookkeeping
(`planningTaskIds.delete`) **stays synchronous** on purpose — deferring
*that* would let a second update re-enter the branch.

## The union is load-bearing, not defensive

Post-U11 the default lineage has no `triage` column, so a **resolved**
answer returns `intake: "todo"` where the inert path fell back to
`"triage"`. Converting without unioning the legacy ids silently
**narrowed** the wake set and stopped waking cards in a legacy-named
lane — caught by *"schedules when planning clears in triage"*.

**A resolved conversion must be a superset of what it replaces, or it is
a behaviour change wearing a vocabulary change's clothes.** That's the
reusable lesson here.

## Tests

- Drained with the repo's existing **`flushAsyncHandlers`** helper —
written for exactly this fire-and-forget shape — rather than loosening
any assertion.
- **The characterization test flipped, as designed.**
`workflow-scheduler-parked-columns-live-e2e.pg.test.ts` asserted *"a
dependent in a RENAMED hold column is NEVER unblocked"*, with its author
noting: *"expected to flip to null the moment the resolver is fixed —
and that flip is the whole point of writing it down."* It flipped.
Inverted to a REGRESSION case so the assertion holds the fix rather than
the defect; it now matches its own CONTROL arm, which still guards
against a vacuous pass.

## Verification

- 21 scheduler suites — **361 green**, including the live PostgreSQL e2e
- **`pnpm test:gate` green**; eslint and `tsc` clean
- Changeset added; `check:changesets` passes

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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 05:41:16 -07:00
..