U12's headline goal. The raw `experimentalFeatures.workflowColumns` flag gated **every task move**; its six seams are now unconditional and the flag, its last two readers, and the 124-line inline legacy branch are deleted. ## Deleted, not converted The flag-OFF branch goes with the gate. Converting a branch we intended to delete would have left a second definition of every column side effect alive to drift — the defect this program has spent its length removing. Both readers flip in **one commit** because they are not separable: the preflight in `workflow-task-create-ops.ts` computes the `movePolicyPreflight` that `moves.ts` consumes and validates. Un-gating either alone either evaluates workflow move policies — with their plugin-gate side effects — whose result is ignored, or validates against a preflight that was never computed. ## Evidence, not assertion **Equivalence (precondition 1).** `moves-flag-equivalence.test.ts` (commit 1) ran the same journey under both flag states against live PostgreSQL and diffed the persisted row: **identical across 128 fields** plus an equal timing shape, over `todo → in-progress → in-review → todo → in-progress`. Mutation-verified both ways — stamping the flag-ON branch, and diverging the reopen hook, each fail it. **And there was stronger evidence already on main that isn't mine.** U2b's `move-path-equivalence.pg.test.ts` ran *every* scenario once per path and has been green across ~10 of them: `preserveStatus`, `preservePause`, timing accounting, `preserveProgress`, `preserveWorktree`, engine-source rehome, `in-progress → todo`. Two independently built harnesses agreeing is the best evidence this question has had. **The flag was read by nothing in production.** `experimentalFeatures` is global-only and no module writes it, so this path had never run for any project without a stale persisted value. That is also why a green suite was never evidence on its own — both paths were individually valid and only one was live. ## Two claims of mine this PR corrects **1. Seam 2 does not introduce new rejections.** I said in #2639 and in the census that with the flag off there is *no* target validation, so flipping would add refusals. Reproduced the opposite: a move to an undeclared column already rejects on the legacy path with `Invalid transition: … Valid targets: …`. I found it because the discriminator I wrote to prove "the flag is the cause" failed. **2. My first equivalence test proved nothing.** It used `updateSettings`; `experimentalFeatures` is **global-only**, so `getSettingsFast()` filtered the write out and `useWorkflow` was false in *both* runs. Caught by stamping the flag-ON branch and watching the test stay green. It now writes via `updateGlobalSettings` and **asserts the flag took effect** before the journey. U2b's harness carries the same warning independently — `MUST be updateGlobalSettings, NOT updateSettings`. ## The user-visible change Move rejections now report **workflow-resolved** targets instead of the hardcoded legacy adjacency table. Concretely: `Valid targets: in-progress, triage, archived` becomes `Valid targets: archived, in-progress`. That is the fix, not a regression — the legacy table still advertised `triage`, a column the default lineage stopped declaring at #2515, so an operator following the old message was told to move somewhere impossible. Likewise a move *into* `triage` is now refused rather than stranding the card in a column with no trait flags, invisible to every trait-driven sweep until reconciliation re-homes it. `live-move-path-undeclared-target.test.ts` characterised exactly that defect and carried `it.todo("should REFUSE a move into a column the task's workflow does not declare (U2b)")` — **this fulfils it.** ## Test migration | file | change | |---|---| | `move-path-equivalence.pg.test.ts` | deleted — every scenario ran once per path; purpose fully discharged | | `workflow-capacity-invariant.pg.test.ts` | `setPath("inline"\|"hooks")` → `assertMovePathLive()`; the probe is **kept** so capacity cannot pass because moves were broken for an unrelated reason | | `store-movement.pg.test.ts` | asserts the refusal **and** that the legitimate backward move still works, so it reads as a narrowing | | `raw-workflow-columns-flag-census.test.ts` | deleted per its own instructions — it was built to fail in both directions and fired exactly as designed: `expected [] to deeply equal [3 readers]` | | `moves-workflow-flag-seams.test.ts` | deleted — it pinned the six seams this removes | ## Verification Full core suite: **33 failed / 10 files — byte-identical to main's baseline**, with **zero** files failing exclusively on this branch. I measured the baseline by checking out `origin/main` and running the same command, because the first comparison I made was by count alone and would have blamed the flip for 8 files that were already red. `pnpm lint` clean. `pnpm test:gate` green (10 / 132 / 482 / 71). `tsc -p packages/core/tsconfig.json` clean. Core builds. ## Left in place deliberately The `workflowColumns` settings key stays schema-tolerated and is already in `HIDDEN_EXPERIMENTAL_FEATURE_KEYS`, so an upgraded project carrying a stale value renders nothing and loads cleanly. Removing it from the schema would risk rejecting those projects for no benefit now that nothing reads it. --- ## Rebased onto current main — and `triage` reaches ZERO | metric | before | after | |---|---:|---:| | `moves.ts` column guards | 39 | **15** | | repo column total | 745 | **741** | | **`triage` column guards** | 1 | **ABSENT (0)** | `triage` is now absent from `byColumnId` entirely: no unconverted `triage` guard remains anywhere in production source. Combined with #2664 (the last one, in `TaskContextMenu`) this closes bar item 1. The census behaved exactly as designed on the rebase: the flip *deletes* guards, so `--strict` reported `moves.ts: allows 39, tree has 15` rather than leaving a stale allowance, and the re-record lands in this PR's diff. ## Verification on the rebased tree - Full core suite: **33 failed / 10 files — identical to main's baseline**, zero files failing exclusively on this branch (measured by checking out `origin/main` and diffing the failing-file sets, not by comparing counts). - `pnpm test:gate` green (10 / 158 / 487 / 71). - `pnpm check:lifecycle-columns` exits 0. - `pnpm lint` clean, `@fusion/core` builds. ## Two review fixes carried in this PR **P1 — optionless engine moves lost their bypass.** `resolveWorkflowBypassGuardsImpl` did `void moveSource;` — it discarded the resolved parameter and re-read `options?.moveSource`, so `moveTask(id, target)` resolved to `"engine"` at the call site and computed `bypassGuards === false`. Latent while the flag gated validation; with the gate gone, an internal executor/merger/recovery move made without an options object would be judged as a user move. **P2 — the absence signal.** Emitting `workflowId` unconditionally would have stamped `builtin:coding` onto every task with no explicit selection, reporting a fallback as authoritative. Now emits the selection directly, so absent still means "not resolved here". <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Task moves are now validated against the task’s declared workflow. - Invalid destinations are rejected with a clear error, and tasks remain in their original column. - Valid backward moves continue to work as expected. - Move behavior and lifecycle updates are now handled consistently across workflows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
211 lines
10 KiB
JSON
211 lines
10 KiB
JSON
{
|
|
"generatedFrom": "node scripts/lifecycle-column-census.mjs --strict --update-baseline",
|
|
"totals": {
|
|
"column": 722,
|
|
"role": 5,
|
|
"status": 186,
|
|
"deliberate": 17
|
|
},
|
|
"byColumnId": {
|
|
"done": 195,
|
|
"in-progress": 138,
|
|
"in-review": 200,
|
|
"archived": 147,
|
|
"todo": 42
|
|
},
|
|
"byFile": {
|
|
"packages/engine/src/self-healing.ts": 110,
|
|
"packages/engine/src/executor.ts": 85,
|
|
"packages/dashboard/app/components/TaskCard.tsx": 42,
|
|
"packages/dashboard/app/components/TaskDetailModal.tsx": 30,
|
|
"packages/engine/src/scheduler.ts": 28,
|
|
"packages/dashboard/src/routes/register-task-workflow-routes.ts": 20,
|
|
"packages/core/src/task-store/moves.ts": 15,
|
|
"packages/core/src/store.ts": 12,
|
|
"packages/engine/src/project-engine.ts": 12,
|
|
"packages/engine/src/mission-execution-loop.ts": 10,
|
|
"packages/core/src/task-store/async-comments-attachments.ts": 9,
|
|
"packages/dashboard/app/components/TaskContextMenu.tsx": 9,
|
|
"packages/dashboard/src/github-tracking-comments.ts": 9,
|
|
"packages/dashboard/src/github-tracking-reconciler.ts": 9,
|
|
"packages/engine/src/notification/notification-service.ts": 9,
|
|
"packages/cli/src/commands/dashboard.ts": 8,
|
|
"packages/cli/src/commands/task.ts": 8,
|
|
"packages/core/src/default-workflow-hooks.ts": 7,
|
|
"packages/core/src/task-store/update-task-deps.ts": 7,
|
|
"packages/dashboard/app/components/Column.tsx": 7,
|
|
"packages/core/src/live-agent-count.ts": 6,
|
|
"packages/core/src/task-merge.ts": 6,
|
|
"packages/core/src/task-store/branch-group-ops.ts": 6,
|
|
"packages/core/src/task-store/task-artifacts-ops.ts": 6,
|
|
"packages/dashboard/app/components/ListView.tsx": 6,
|
|
"packages/dashboard/src/reliability-metrics.ts": 6,
|
|
"packages/engine/src/runtimes/in-process-runtime.ts": 6,
|
|
"packages/cli/src/extension.ts": 5,
|
|
"packages/core/src/task-store/merge-queue-ops-2.ts": 5,
|
|
"packages/dashboard/app/hooks/useTaskDiffStats.ts": 5,
|
|
"packages/engine/src/agent-tools.ts": 5,
|
|
"packages/engine/src/merger.ts": 5,
|
|
"packages/engine/src/restart-recovery-coordinator.ts": 5,
|
|
"packages/core/src/agent-store.ts": 4,
|
|
"packages/core/src/blocker-fanout.ts": 4,
|
|
"packages/core/src/task-age-staleness.ts": 4,
|
|
"packages/core/src/task-store/task-store-helpers.ts": 4,
|
|
"packages/dashboard/app/components/TaskReviewTab.tsx": 4,
|
|
"packages/dashboard/app/components/taskSorting.ts": 4,
|
|
"packages/dashboard/src/gitlab-tracking-comments.ts": 4,
|
|
"packages/dashboard/src/routes/register-git-github.ts": 4,
|
|
"packages/engine/src/agent-heartbeat.ts": 4,
|
|
"packages/engine/src/replan-target.ts": 4,
|
|
"packages/engine/src/triage.ts": 4,
|
|
"packages/engine/src/usage-limit-detector.ts": 4,
|
|
"packages/core/src/async-mission-store-queries.ts": 3,
|
|
"packages/core/src/async-mission-store.ts": 3,
|
|
"packages/core/src/task-priority.ts": 3,
|
|
"packages/core/src/task-store/async-merge-coordination.ts": 3,
|
|
"packages/core/src/task-store/task-update.ts": 3,
|
|
"packages/dashboard/app/components/DockTaskList.tsx": 3,
|
|
"packages/dashboard/app/components/TaskChangesTab.tsx": 3,
|
|
"packages/dashboard/app/components/TaskChatTab.tsx": 3,
|
|
"packages/dashboard/app/utils/taskActivity.ts": 3,
|
|
"packages/dashboard/app/utils/worktreeGrouping.ts": 3,
|
|
"packages/dashboard/src/chat.ts": 3,
|
|
"packages/dashboard/src/routes/register-chat-routes.ts": 3,
|
|
"packages/engine/src/gridlock-detector.ts": 3,
|
|
"packages/engine/src/planner-overseer.ts": 3,
|
|
"packages/engine/src/worktree-pool.ts": 3,
|
|
"packages/cli/src/commands/dashboard-tui/app.tsx": 2,
|
|
"packages/core/src/assigned-task-ranking.ts": 2,
|
|
"packages/core/src/duplicate-intake.ts": 2,
|
|
"packages/core/src/near-duplicate-canonical.ts": 2,
|
|
"packages/core/src/node-override-guard.ts": 2,
|
|
"packages/core/src/task-move-disposer.ts": 2,
|
|
"packages/core/src/task-store/archive-lifecycle-2.ts": 2,
|
|
"packages/core/src/task-store/audit-ops.ts": 2,
|
|
"packages/core/src/task-store/comments-ops.ts": 2,
|
|
"packages/core/src/task-store/project-store-ops.ts": 2,
|
|
"packages/core/src/task-store/reads.ts": 2,
|
|
"packages/core/src/task-store/symbol-locks.ts": 2,
|
|
"packages/core/src/task-store/task-id-integrity.ts": 2,
|
|
"packages/core/src/team-analytics.ts": 2,
|
|
"packages/core/src/workflow-analytics.ts": 2,
|
|
"packages/dashboard/app/components/Board.tsx": 2,
|
|
"packages/dashboard/app/components/DocumentsView.tsx": 2,
|
|
"packages/dashboard/app/components/effective-model-resolution.ts": 2,
|
|
"packages/dashboard/app/components/WorkflowResultsTab.tsx": 2,
|
|
"packages/dashboard/app/utils/prFeedback.ts": 2,
|
|
"packages/dashboard/app/utils/taskRevert.ts": 2,
|
|
"packages/dashboard/app/utils/taskTiming.ts": 2,
|
|
"packages/dashboard/src/github-tracking-state.ts": 2,
|
|
"packages/dashboard/src/routes/register-project-routes.ts": 2,
|
|
"packages/dashboard/src/routes/register-session-diff-routes.ts": 2,
|
|
"packages/dashboard/src/routes/register-system-maintenance-routes.ts": 2,
|
|
"packages/dashboard/src/server.ts": 2,
|
|
"packages/engine/src/agent-reflection.ts": 2,
|
|
"packages/engine/src/auto-merge-finalization.ts": 2,
|
|
"packages/engine/src/cli-agent/state-machine.ts": 2,
|
|
"packages/engine/src/merger-scope-auto-widen.ts": 2,
|
|
"plugins/fusion-plugin-even-cards/src/cards/board-cards.ts": 2,
|
|
"plugins/fusion-plugin-reports/src/store/report-store.ts": 2,
|
|
"packages/cli/src/commands/pr.ts": 1,
|
|
"packages/core/src/eval-automation.ts": 1,
|
|
"packages/core/src/eval-signal-collector.ts": 1,
|
|
"packages/core/src/in-review-stall.ts": 1,
|
|
"packages/core/src/mission-store.ts": 1,
|
|
"packages/core/src/plugin-store.ts": 1,
|
|
"packages/core/src/stalled-review-detector.ts": 1,
|
|
"packages/core/src/task-store/branch-and-pr-entities.ts": 1,
|
|
"packages/core/src/task-store/lifecycle-ops.ts": 1,
|
|
"packages/core/src/task-store/merge-queue-ops.ts": 1,
|
|
"packages/core/src/task-timing.ts": 1,
|
|
"packages/dashboard/app/components/ChangesDiffModal.tsx": 1,
|
|
"packages/dashboard/app/components/command-center/liveSnapshotMetrics.ts": 1,
|
|
"packages/dashboard/app/components/DevServerView.tsx": 1,
|
|
"packages/dashboard/app/components/MergeDetails.tsx": 1,
|
|
"packages/dashboard/app/components/PrPanel.tsx": 1,
|
|
"packages/dashboard/app/components/ResearchTaskActionModal.tsx": 1,
|
|
"packages/dashboard/app/components/RoutingTab.tsx": 1,
|
|
"packages/dashboard/app/components/TaskPlannerChatTab.tsx": 1,
|
|
"packages/dashboard/app/hooks/useExecutorStats.ts": 1,
|
|
"packages/dashboard/app/hooks/useTasks.ts": 1,
|
|
"packages/dashboard/app/utils/inReviewStallCopy.ts": 1,
|
|
"packages/dashboard/app/utils/quickAddStart.ts": 1,
|
|
"packages/dashboard/app/utils/stalePausedReviewCopy.ts": 1,
|
|
"packages/dashboard/app/utils/taskStuck.ts": 1,
|
|
"packages/dashboard/src/github-issue-comment.ts": 1,
|
|
"packages/dashboard/src/gitlab-issue-comment.ts": 1,
|
|
"packages/dashboard/src/gitlab-source-issue-reconciler.ts": 1,
|
|
"packages/dashboard/src/knowledge-index-refresh.ts": 1,
|
|
"packages/dashboard/src/planning-board-tools.ts": 1,
|
|
"packages/dashboard/src/research-routes.ts": 1,
|
|
"packages/dashboard/src/routes/register-agent-core-routes.ts": 1,
|
|
"packages/dashboard/src/task-planner-chat-context.ts": 1,
|
|
"packages/dashboard/src/task-planner-chat-metrics.ts": 1,
|
|
"packages/dashboard/src/test/mockCoreEngine.ts": 1,
|
|
"packages/engine/src/auto-recovery-handlers/branch-worktree.ts": 1,
|
|
"packages/engine/src/backlog-pressure-reporter.ts": 1,
|
|
"packages/engine/src/cli-agent/task-session.ts": 1,
|
|
"packages/engine/src/ephemeral-worker-manager.ts": 1,
|
|
"packages/engine/src/merger-integration-worktree.ts": 1,
|
|
"packages/engine/src/merger-orphan-rehome.ts": 1,
|
|
"packages/engine/src/plugin-runner.ts": 1,
|
|
"packages/engine/src/pr-comment-handler.ts": 1,
|
|
"plugins/fusion-plugin-even-realities-glasses/src/notifications/diff.ts": 1,
|
|
"plugins/fusion-plugin-reports/src/store/report-types.ts": 1
|
|
},
|
|
"deliberateByFile": {
|
|
"packages/dashboard/app/components/TaskCard.tsx\u0000triage": 2,
|
|
"packages/dashboard/app/components/TaskDetailModal.tsx\u0000triage": 2,
|
|
"packages/dashboard/app/components/command-center/MissionControlPanel.tsx\u0000done": 1,
|
|
"packages/dashboard/app/components/command-center/MissionControlPanel.tsx\u0000in-review": 1,
|
|
"packages/dashboard/app/components/command-center/MissionControlPanel.tsx\u0000todo": 1,
|
|
"packages/dashboard/app/components/TaskCard.tsx\u0000todo": 1,
|
|
"packages/dashboard/app/components/TaskContextMenu.tsx\u0000triage": 1,
|
|
"packages/dashboard/app/components/TaskDetailModal.tsx\u0000todo": 1,
|
|
"packages/dashboard/app/utils/columnRoles.ts\u0000todo": 1,
|
|
"packages/dashboard/src/routes/register-task-workflow-routes.ts\u0000todo": 1,
|
|
"packages/dashboard/src/routes/register-task-workflow-routes.ts\u0000triage": 1,
|
|
"packages/engine/src/hold-release.ts\u0000archived": 1,
|
|
"packages/engine/src/hold-release.ts\u0000done": 1,
|
|
"packages/engine/src/hold-release.ts\u0000in-review": 1,
|
|
"packages/engine/src/triage.ts\u0000triage": 1
|
|
},
|
|
"properties": {
|
|
"query": 83,
|
|
"definition": 43
|
|
},
|
|
"queryByColumnId": {
|
|
"todo": 10,
|
|
"archived": 7,
|
|
"done": 10,
|
|
"in-progress": 19,
|
|
"in-review": 35,
|
|
"triage": 2
|
|
},
|
|
"queryByFile": {
|
|
"packages/engine/src/self-healing.ts": 48,
|
|
"packages/engine/src/project-engine.ts": 7,
|
|
"packages/engine/src/backlog-pressure-reporter.ts": 3,
|
|
"packages/core/src/task-store/async-persistence.ts": 2,
|
|
"packages/core/src/task-store/merge-queue-ops.ts": 2,
|
|
"packages/core/src/task-store/moves.ts": 2,
|
|
"packages/engine/src/executor.ts": 2,
|
|
"packages/engine/src/stale-task-reporter.ts": 2,
|
|
"packages/cli/src/extension.ts": 1,
|
|
"packages/core/src/async-mission-store.ts": 1,
|
|
"packages/core/src/eval-automation.ts": 1,
|
|
"packages/core/src/store.ts": 1,
|
|
"packages/core/src/task-store/archive-lifecycle-2.ts": 1,
|
|
"packages/core/src/task-store/async-archive-lineage.ts": 1,
|
|
"packages/core/src/task-store/async-self-healing.ts": 1,
|
|
"packages/core/src/task-store/task-artifacts-ops.ts": 1,
|
|
"packages/core/src/task-store/task-store-helpers.ts": 1,
|
|
"packages/dashboard/src/routes/register-gitlab.ts": 1,
|
|
"packages/engine/src/agent-tools.ts": 1,
|
|
"packages/engine/src/auto-merge-finalization.ts": 1,
|
|
"packages/engine/src/restart-recovery-coordinator.ts": 1,
|
|
"packages/engine/src/scheduler.ts": 1,
|
|
"packages/engine/src/workflow-node-handlers.ts": 1
|
|
}
|
|
}
|