The **sixth** instance of the async-memo shape #2998 documents — and the
only one that **loses work** rather than mis-rendering.
`handleDrop` gates the "Preserve Progress?" confirmation on the lane's
role:
```js
const shouldPrompt = hasStepProgress && isPreImplementationColumnRole(columnFlags, column);
if (shouldPrompt) { const keepProgress = await confirm({ … }); … }
```
…but its `useCallback` deps were `[addToast, allTasks, column, confirm,
onMoveTask, tasks, t]` — no `columnFlags`. The board resolves workflow
traits after first paint, so the DOM keeps the closure built during the
pre-load render, where `columnFlags` is `undefined` and the helper falls
back to `LEGACY_PRE_IMPLEMENTATION_COLUMN_IDS`. A renamed intake lane is
not a member.
**Result: a card with completed steps dropped into that lane moves with
`shouldPrompt === false`.** The user is never offered "Keep Progress",
and the steps are reset silently.
## How this was found
It is the last unverified candidate from the derivation-aware scan I
posted on #2998, where I explicitly declined to file it as a bug without
checking. Checking it is what turned it from a scanner hit into this.
## Severity, stated honestly
`allTasks` and `tasks` are in the dep list and change identity on any
task-list refresh, so the stale closure is rebuilt within seconds on a
busy board. The exposure is the quiet gap right after the traits land —
**bounded**, like the near-duplicate chip (#2997), not permanent like
the ticker (#2996) whose only refreshing dependency fired at local
midnight.
Bounded still matters here because the cost is not a wrong pixel: it is
completed steps discarded without a prompt, and the window is exactly
when someone has just opened a board and starts dragging.
## Verification
| state | result |
|---|---|
| clean | 2/2 pass |
| revert `columnFlags` from the deps | **1 failed / 1 passed** |
The paired negative asserts a non-pre-implementation lane still moves
**without** prompting — a fix that prompts everywhere turns the dialog
into noise that gets clicked through, costing the same progress it
protects.
The observable is `confirm`, not `onMoveTask`: whether the user was
*asked* is the contract, and asserting on the move alone passes either
way.
`tsc -p tsconfig.app.json` 0 errors in the new file, lint clean, FNXC
gate exit 0, 4/4 across both `Column` flags-arrival suites.
## Running tally of this shape
ticker (#2996) · near-duplicate chip (#2997) · fan-out trait index
(#2993) · merge signature (#3001) · lifecycle dates (#3007) · this. Six,
in two components plus the fan-out path. #3001 called the merge
signature "the last live site"; it was the last of *that* sweep's nine
candidates, and two more have surfaced since from a different scan.
Worth knowing before anyone declares the class closed again.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>