U11 fallout: disprove the coding-ideas column collapse, and correct a U11 note that recorded the merge backwards (#2651)
Two findings, no behavior change. Both are about **recorded reasoning that was wrong** — the kind that sends the next person the wrong way. ## 1. The coding-ideas column collapse does not work (IR change reverted) I implemented it — deleted `ideas`, moved its `intake`/`autoTriage: false` onto Planning, repointed the `start` anchor, updated the IR suites to the merged shape (they went green, 44/44). Then the wider suites failed and showed why it cannot work. **The manual gate IS the column boundary.** `replan-target.ts` names the discriminator in its own comment: *"The real discriminator is which lane the triage service SCANS, which depends on the intake column's `autoTriage` config."* So `ideas` is unscanned, `todo` is scanned, and "promote" means moving the card from one into the other. Merge them and one column must be both: | if… | consequence | |---|---| | `autoTriage: false` wins | never scanned → nothing is ever planned → the capacity hold releases an **unplanned** card into `in-progress`, violating FN-7648 | | scanning wins | `autoTriage: false` is meaningless → the manual gate is gone → the preset duplicates the default Coding workflow | **8 tests fail, and they are not fixtures** — they encode the promotion flow itself, e.g. `store-create-intake-column.test.ts` › *"promotes an Ideas-parked task to todo without planning it (still bootstrap-stub PROMPT.md)"*. Rewriting them would have meant inventing what "promote" means with no destination column, which is how a broken flow gets blessed by a green suite. **What it would actually take:** a promoted flag the triage scan reads, so one column can hold both "not yet promoted" and "being planned". That is a new lifecycle signal, not a column merge — the same shape as the deferred `needs-replan` follow-up. Happy to scope it. **I also corrected my own earlier checklist** in this doc, which said to delete the now-dead `isUnplannedStartCreate` arm. Wrong: `autoTriage` is a general trait field (`builtin-traits.ts`), so any custom workflow can declare a manual intake with `intake !== hold`. The arm is dead only for this preset. ## 2. `replan-target.ts` recorded the U11 merge backwards The note claimed U11 deletes `todo` and keeps `triage`. It is the reverse — Shape B kept the id `todo` and deleted `triage`, precisely so the ~120 `column === "todo"` guards kept their meaning and no data migration shipped. The default lineage now declares `todo, in-progress, in-review, done, archived`. The lookups are correct today, but **for the opposite reason to the one recorded**: the default lineage falls *through* the `triage` lookup and lands on `todo`, its merged planning column. `triage` still matches the workflows that genuinely declare it (Lead generation, PR review). Also flagged without changing (it would be a behavior change): the `return "triage"` fallbacks on the no-match and throw paths name a column the default lineage no longer declares, so a workflow with neither `triage` nor `todo` gets a nonexistent target. ## Census **Unchanged: 781 total, triage 5.** This PR adds no guards and converts none — `workflowHasColumn(ir, "triage")` is a call argument, not a comparison, so it is outside what the census counts either way. ## Verification 41/41 engine replan-target suites (including the existing `replan-target-merged-planning-column` suite that covers the corrected behavior) · engine typecheck clean · the reverted IR restores the tree to main's content for those three files, verified by `git checkout --`. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -251,3 +251,40 @@ Four passing cases pin what a fix must NOT break: every declared lifecycle move
|
||||
undeclared column on purpose (the path that rescues already-stranded cards). Plus a premise test
|
||||
asserting the compatibility flag really is unset, so the suite fails loudly if that ever changes
|
||||
rather than silently testing a different code path.
|
||||
|
||||
### CORRECTION (same day): the collapse is NOT mechanical — it is contradictory
|
||||
|
||||
I implemented the checklist above, ran the suites, and it does not work. Recording the disproof
|
||||
because the checklist made it look like a fixture update.
|
||||
|
||||
The two-column shape works because the triage service SCANS one lane and skips the other.
|
||||
`replan-target.ts` names the discriminator exactly:
|
||||
|
||||
> The real discriminator is which lane the triage service SCANS, which depends on the intake
|
||||
> column's `autoTriage` config, not on the intake/hold roles alone.
|
||||
|
||||
So in Coding (Ideas): `ideas` (intake, `autoTriage: false`) is NOT scanned, `todo` IS, and "promote"
|
||||
means moving the card from the unscanned lane into the scanned one. That move is the gate release.
|
||||
|
||||
Merge them and one column must be both the unscanned manual intake AND the scanned planning lane.
|
||||
There is no consistent answer:
|
||||
|
||||
- `autoTriage: false` wins -> the column is never scanned, so nothing is ever planned. Cards sit
|
||||
with a bootstrap-stub PROMPT.md until the capacity hold releases them, which sends an UNPLANNED
|
||||
card into `in-progress` — the FN-7648 invariant ("no unplanned card enters a processing column").
|
||||
- scanning wins -> `autoTriage: false` means nothing, the manual gate is gone, and the preset is a
|
||||
duplicate of the default Coding workflow.
|
||||
|
||||
Evidence: 8 tests fail, and they are not fixtures — they encode the promotion flow itself, e.g.
|
||||
`store-create-intake-column.test.ts` › "promotes an Ideas-parked task to todo without planning it
|
||||
(still bootstrap-stub PROMPT.md)". Rewriting them would have required inventing what "promote" means
|
||||
with no destination column.
|
||||
|
||||
**What the collapse actually requires:** a release mechanism for a manual gate that is not a column
|
||||
move — a per-task promoted flag the triage scan reads, so one column can hold both "not yet
|
||||
promoted" and "promoted, being planned". That is a new lifecycle signal, not a column merge, and it
|
||||
is the same shape as the deferred `needs-replan`-to-purpose-built-signal follow-up.
|
||||
|
||||
Also correcting step 4 of the checklist above: do NOT delete the `isUnplannedStartCreate` arm.
|
||||
`autoTriage` is a general trait field (`builtin-traits.ts`), so any custom workflow can declare a
|
||||
manual intake with `intake !== hold`. The arm is dead only for THIS preset, not in general.
|
||||
|
||||
@@ -332,10 +332,20 @@ export async function resolveReplanTargetColumn(store: TaskStore, taskId: string
|
||||
alone. Resolving that correctly is its own change with its own evidence; it is
|
||||
deliberately not smuggled into a conversion PR.
|
||||
|
||||
U11 IMPACT, flagged rather than assumed: the second lookup asks for `"todo"`,
|
||||
which U11 deletes. For builtin coding the first lookup still matches `triage`
|
||||
(which U11 keeps), so the builtins stay correct — but a workflow relying on the
|
||||
`todo` branch loses it. That is a real follow-up, not a blocker for this PR.
|
||||
U11 IMPACT — CORRECTED 2026-07-29-23:59. The note here previously said U11 deletes `todo` and
|
||||
keeps `triage`. It is the other way round: U11 chose "keep the id `todo`, delete `triage`" so
|
||||
that the ~120 `column === "todo"` guards kept their meaning and no data migration shipped. The
|
||||
default lineage now declares `todo, in-progress, in-review, done, archived` and NO `triage`.
|
||||
|
||||
The lookups below are therefore correct today, but for the opposite reason to the one recorded:
|
||||
the default lineage FALLS THROUGH the `triage` lookup and lands on `todo`, which is its merged
|
||||
planning column and the right replan target. `triage` still matches for the workflows that
|
||||
genuinely declare it (Lead generation, PR review — where it is the intake/planning lane).
|
||||
|
||||
STILL A REAL FOLLOW-UP, unchanged by the correction: the `return "triage"` fallbacks on the
|
||||
no-match and throw paths name a column the default lineage no longer declares, so a workflow
|
||||
with neither `triage` nor `todo` is handed a nonexistent target. Behavior change, so it is
|
||||
flagged rather than fixed in a comment correction.
|
||||
*/
|
||||
if (workflowHasColumn(ir, "triage")) return "triage";
|
||||
if (workflowHasColumn(ir, "todo")) return "todo";
|
||||
|
||||
Reference in New Issue
Block a user