Files
fusion/packages/engine
gsxdsm 5cb731420c test(engine): re-point the stale-spec ratchet at the improved guard (#2863)
## Main is red; this fixes it

`executor-stale-spec-active-lanes.test.ts` — 2 failures on `main`:

```
× resolves the task's lifecycle columns before deciding the skip
   → expected source to contain 'const activeLifecycle = resolveLifecy…'
× adds the wip, review and complete lanes to the active set
```

**Nothing regressed — the guard got better and the ratchet didn't
follow.**

## What changed in the product

The stale-spec skip used to build its active set from
`resolveLifecycleColumns(...)`, taking `?.wip`, `?.review` and
`?.complete`. That returns the **first** column carrying each trait, so
a board with two wip lanes — or a review lane plus a second
merge-blocking one — had only one of each recognised as active. A card
in the other read as **inactive**, and its prompt file was treated as
reclaimable.

It now resolves the IR once and unions `columnsWithFlag` over five
flags, which returns **every** column carrying each:

```ts
const activeIr = await resolveWorkflowIrForTask(this.store, task.id);
const activeColumns = new Set<string>(["in-progress", "in-review", "done"]);
if (activeIr) {
  for (const flag of ["countsTowardWip", "mergeOrchestration", "mergeBlocker", "humanReview", "complete"] as const) {
    for (const lane of columnsWithFlag(activeIr, flag)) activeColumns.add(lane);
```

That is a real fix to an arity bug, and the legacy trio stays unioned in
for the documented reason: under-reporting active is the destructive
direction.

## Why re-point rather than loosen

This is a **source ratchet** in the `engine-no-blocking-shellout` style,
and its entire value is that it fails on a revert. A substring loose
enough to match both the old and new shapes would keep the file green
through exactly the regression it exists to catch.

So the assertions now name the new shape precisely, including the **flag
list**, so dropping one of the five is caught here too.

**Mutation-verified:** replacing the IR resolution with `undefined`
fails the ratchet. It still does its job.

## Scope

The file's own header notes this is *not* a behavioural proof — the
guard sits inside `execute()` behind worktree and session setup a unit
test has no business standing up, and it asks whoever next touches that
scaffolding to add the end-to-end case. Re-pointing is maintenance; I
have not taken on that harness here, and the note still stands.

## Verification

- `executor-stale-spec-active-lanes.test.ts` — **4/4**, fails on revert
- `pnpm lint` — clean

Test-only; no changeset. Found by running the full `engine-default`
project after #2855 merged — the remaining `main` red is my own audit
case, fixed by the pending #2857.

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

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