From 0c07584d51ae4b96ba4f78285b54f46f63ba4ded Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 29 Jul 2026 23:26:49 -0700 Subject: [PATCH] U11 fallout: disprove the coding-ideas column collapse, and correct a U11 note that recorded the merge backwards (#2651) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../u11-triage-literal-safety-audit.md | 37 +++++++++++++++++++ packages/engine/src/replan-target.ts | 18 +++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/docs/solutions/architecture-patterns/u11-triage-literal-safety-audit.md b/docs/solutions/architecture-patterns/u11-triage-literal-safety-audit.md index 8c508fb3c3..16fe2fc814 100644 --- a/docs/solutions/architecture-patterns/u11-triage-literal-safety-audit.md +++ b/docs/solutions/architecture-patterns/u11-triage-literal-safety-audit.md @@ -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. diff --git a/packages/engine/src/replan-target.ts b/packages/engine/src/replan-target.ts index 07a7cd7a2c..5ea507192b 100644 --- a/packages/engine/src/replan-target.ts +++ b/packages/engine/src/replan-target.ts @@ -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";