Follow-up to #2785. Nine engine files to **zero**, all one question — *"is this task finished?"* — asked in nine places, wrong in every one on a renamed board. ## Census, per file (measured, `--strict` verified) | file | main | here | | --- | ---: | ---: | | `agent-reflection.ts` | 2 | **0** | | `merger-scope-auto-widen.ts` | 2 | **0** | | `worktree-pool.ts` | 2 | **0** | | `cli-agent/state-machine.ts` | 2 | **0** (reclassified — see below) | | `auto-recovery-handlers/branch-worktree.ts` | 1 | **0** | | `cli-agent/task-session.ts` | 1 | **0** (reclassified) | | `merger-integration-worktree.ts` | 1 | **0** | | `merger-orphan-rehome.ts` | 1 | **0** | | `plugin-runner.ts` | 1 | **0** | | **net** | | **−13** | ## The one worth reading: `branch-worktree` had TWO defects, and the census could only see one ```ts if (task.column === "in-progress") { …clear branch… } // counted await this.deps.taskStore.moveTask(task.id, "todo", { … }); // INVISIBLE ``` The census scores **comparisons**. The requeue *destination* is a call argument, so nothing in the backlog ever pointed at it — and it is the worse of the two: a board with no `todo` column was requeued into a lane **that does not exist**. The counted literal is the smaller half (a renamed wip lane meant the stale branch was never cleared, so the card carried a dead branch back into execution). Converting the comparison alone would have dropped a census count and left the board requeuing into nowhere. Destination now resolves through `resolveReboundTarget` (KTD-10 ordering: hold → intake → first column). Reverted **independently**: destination restored → 2 fail (`moveTask` called with `"todo"`, not `"backlog"`); wip test restored → 1 fail (`updateTask` never called). ## The rest - **`plugin-runner`** — `onTaskCompleted` never fired on a renamed board. Every plugin that closes an issue, posts a notification, or records a metric on completion **silently stopped**, with nothing logged. Resolved *asynchronously* inside the existing fire-and-forget seam, not via `resolveTaskWorkflowIrSync` — per `sync-workflow-ir-callsite-allowlist` that reader returns the DEFAULT workflow for every task in production, so a sync guard here would read as converted and still be wrong. The listener is already `void`-dispatched, so awaiting inside it changes no observable ordering (the shape `NotificationService` already uses). - **`merger-orphan-rehome`** — a renamed complete lane made every source task read as unfinished, so orphaned commits were never rehomed and stayed stranded off the integration branch. Resolves by the **trailer id**, not `sourceTask.id`, which the fake store does not populate. - **`agent-reflection`** — `classifyOutcome` returned `null` for every finished task, so both callers treated completed work as nothing to reflect on: one recorded `reflection:skipped` with reason `"not-completed"`, the other silently `continue`d. Reflection captured **nothing at all** on a custom board. - **`worktree-pool`** — shipped tasks' worktrees stayed in the ACTIVE set, so the reclaim pass never returned them and the board walks into worktree exhaustion — a stall whose cause is invisible from the symptom. - **`merger-scope-auto-widen`** — finished cards counted as active claimants, so a merge was blocked by a task that no longer exists in any meaningful sense. - **`merger-integration-worktree`** — a shipped task still counted as a live worktree user, so the integration worktree could never be reused and the merge path took the slower rebuild every time. ## The census OVERSTATED the engine backlog by 3 `cli-agent/state-machine.ts` and `cli-agent/task-session.ts` compare against `done` — but that is a **`CliMachineState`** (`ready`/`busy`/`waitingOnInput`/`done`/`resuming`/`idle`) tracking one CLI agent process. It never reads a board column. The census matches the bare string. Marked `DELIBERATE-LITERAL` rather than left for a later sweep to "convert" a process state into a workflow role. Worth flagging fleet-wide: the backlog total includes at least these three non-columns. ## Revert results (measured, each run) | conversion | reverted → | | --- | --- | | `plugin-runner` complete gate | RENAMED case fails — `onTaskCompleted` never invoked | | `merger-orphan-rehome` source gate | RENAMED case fails — `orphan:false, reason:"source-task-not-done"` | | `branch-worktree` destination | 2 fail — `moveTask` called with `"todo"` | | `branch-worktree` wip test | 1 fail — `updateTask` never called | Each has a **non-vacuous companion** (renamed board, non-complete lane / mid-flight source / non-wip column) so a guard that fired unconditionally would not pass. **Four are NOT revert-proven, and I am not claiming otherwise:** `agent-reflection`, `merger-scope-auto-widen`, `merger-integration-worktree`, `worktree-pool`. Their suites omit a workflow and therefore assert the legacy fallback — they pass before and after. `merger-scope-auto-widen` has no test file at all; `scanIdleWorktrees` is mocked in every suite that touches it and driving it for real needs git worktrees on disk. All four strictly **widen** the finished set (resolved roles ∪ the legacy ids), so default boards are byte-identical. That is the argument for shipping them, not a substitute for coverage. ## Examined and deliberately NOT converted - **`backlog-pressure-reporter:173`** — fed by `listTasks({ column: "todo" })`, a hardcoded **query** filter. On a renamed board `todoFull` is empty and the predicate never runs. Converting it drops a census count and changes nothing observable; the fix belongs at the query layer. - **`auto-merge-finalization:28`** — the catch-arm legacy fallback, which must stay for the same reason `columnRoles.ts` keeps its id fallback. - **`auto-merge-finalization:84`** — only selects between two diagnostic reason strings that are **both** `ok: false`, on a pure validator with no store in scope. Converting it would thread a store through a pure function to change a label. ## Merge resolution note Merging main brought conflicts in `agent-assignment.ts` and `ephemeral-worker-manager.ts`. **Main's versions won both** and mine are dropped: main threads an optional `activeColumns` from `scheduler.ts:2340` (a cleaner seam than widening the store type to resolve internally), and its `isAgentIdle` carries a greptile P1 fix mine lacked — `columnsWithFlag` membership rather than first-per-role, so a workflow declaring two wip lanes has both recognised. That is the fourth time in this program main's version of a contested file was the better one. ## Verification - `pnpm test:gate` — 161 + 487 + 13 + 71, green - `npx tsc -p packages/engine/tsconfig.json --noEmit` — clean - `pnpm lint` — clean - `--strict` exits 0 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Workflow-dependent task completion now recognizes custom lifecycle columns, including renamed boards. * Recovery requeues tasks to the configured destination and clears branch details only from the appropriate work-in-progress column. * Improved handling of completed tasks, orphaned work, shared worktrees, and scope evaluation across custom workflows. * Plugin completion hooks now trigger for any column configured as complete. * **Tests** * Added coverage for renamed workflow columns and custom completion, recovery, and rehoming behavior. * **Documentation** * Clarified CLI state terminology in internal developer comments. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
182 lines
10 KiB
JSON
182 lines
10 KiB
JSON
{
|
|
"generatedFrom": "node scripts/lifecycle-column-census.mjs --strict --update-baseline",
|
|
"byFile": {
|
|
"packages/engine/src/self-healing.ts": 97,
|
|
"packages/engine/src/executor.ts": 12,
|
|
"packages/engine/src/scheduler.ts": 12,
|
|
"packages/core/src/task-store/async-comments-attachments.ts": 9,
|
|
"packages/dashboard/app/components/TaskContextMenu.tsx": 9,
|
|
"packages/dashboard/app/components/Column.tsx": 7,
|
|
"packages/dashboard/app/components/ListView.tsx": 6,
|
|
"packages/engine/src/notification/notification-service.ts": 5,
|
|
"packages/engine/src/restart-recovery-coordinator.ts": 5,
|
|
"packages/dashboard/app/components/TaskDetailModal.tsx": 4,
|
|
"packages/engine/src/replan-target.ts": 4,
|
|
"packages/core/src/async-mission-store-queries.ts": 3,
|
|
"packages/core/src/task-store/async-merge-coordination.ts": 3,
|
|
"packages/core/src/task-store/task-artifacts-ops.ts": 3,
|
|
"packages/dashboard/app/components/DockTaskList.tsx": 3,
|
|
"packages/dashboard/app/components/TaskCard.tsx": 3,
|
|
"packages/dashboard/app/components/TaskChangesTab.tsx": 3,
|
|
"packages/dashboard/app/components/TaskChatTab.tsx": 3,
|
|
"packages/dashboard/app/hooks/useTaskDiffStats.ts": 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-task-workflow-routes.ts": 3,
|
|
"packages/engine/src/planner-overseer.ts": 3,
|
|
"packages/core/src/agent-store.ts": 2,
|
|
"packages/core/src/async-mission-store.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/moves.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/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/taskRevert.ts": 2,
|
|
"packages/dashboard/app/utils/taskTiming.ts": 2,
|
|
"packages/dashboard/src/github-tracking-state.ts": 2,
|
|
"packages/dashboard/src/server.ts": 2,
|
|
"packages/engine/src/auto-merge-finalization.ts": 2,
|
|
"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-2.ts": 1,
|
|
"packages/core/src/task-store/merge-queue-ops.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/github-tracking-comments.ts": 1,
|
|
"packages/dashboard/src/gitlab-issue-comment.ts": 1,
|
|
"packages/dashboard/src/gitlab-source-issue-reconciler.ts": 1,
|
|
"packages/dashboard/src/gitlab-tracking-comments.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/routes/register-chat-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/backlog-pressure-reporter.ts": 1,
|
|
"packages/engine/src/ephemeral-worker-manager.ts": 1,
|
|
"packages/engine/src/merger.ts": 1,
|
|
"packages/engine/src/pr-comment-handler.ts": 1,
|
|
"packages/engine/src/runtimes/in-process-runtime.ts": 1,
|
|
"packages/engine/src/triage.ts": 1
|
|
},
|
|
"deliberateByFile": {
|
|
"packages/dashboard/src/reliability-metrics.ts\u0000in-review": 4,
|
|
"packages/core/src/live-agent-count.ts\u0000in-progress": 2,
|
|
"packages/core/src/live-agent-count.ts\u0000in-review": 2,
|
|
"packages/core/src/store.ts\u0000in-review": 2,
|
|
"packages/core/src/task-merge.ts\u0000archived": 2,
|
|
"packages/core/src/task-merge.ts\u0000done": 2,
|
|
"packages/core/src/task-merge.ts\u0000in-review": 2,
|
|
"packages/dashboard/app/components/TaskCard.tsx\u0000triage": 2,
|
|
"packages/dashboard/app/components/TaskDetailModal.tsx\u0000triage": 2,
|
|
"packages/engine/src/cli-agent/state-machine.ts\u0000done": 2,
|
|
"packages/engine/src/scheduler.ts\u0000in-progress": 2,
|
|
"packages/engine/src/scheduler.ts\u0000in-review": 2,
|
|
"packages/engine/src/usage-limit-detector.ts\u0000archived": 2,
|
|
"packages/engine/src/usage-limit-detector.ts\u0000done": 2,
|
|
"plugins/fusion-plugin-reports/src/store/report-store.ts\u0000archived": 2,
|
|
"packages/cli/src/commands/task.ts\u0000archived": 1,
|
|
"packages/cli/src/commands/task.ts\u0000done": 1,
|
|
"packages/cli/src/extension.ts\u0000archived": 1,
|
|
"packages/cli/src/extension.ts\u0000done": 1,
|
|
"packages/core/src/live-agent-count.ts\u0000archived": 1,
|
|
"packages/core/src/live-agent-count.ts\u0000done": 1,
|
|
"packages/core/src/store.ts\u0000archived": 1,
|
|
"packages/core/src/store.ts\u0000done": 1,
|
|
"packages/core/src/store.ts\u0000in-progress": 1,
|
|
"packages/core/src/store.ts\u0000todo": 1,
|
|
"packages/core/src/task-store/task-store-helpers.ts\u0000in-progress": 1,
|
|
"packages/core/src/task-store/task-store-helpers.ts\u0000todo": 1,
|
|
"packages/core/src/task-store/task-update.ts\u0000in-progress": 1,
|
|
"packages/core/src/task-store/task-update.ts\u0000in-review": 1,
|
|
"packages/core/src/task-store/update-task-deps.ts\u0000archived": 1,
|
|
"packages/core/src/task-store/update-task-deps.ts\u0000done": 1,
|
|
"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/RoutineEditor.tsx\u0000triage": 1,
|
|
"packages/dashboard/app/components/ScheduleForm.tsx\u0000triage": 1,
|
|
"packages/dashboard/app/components/ScheduleStepsEditor.tsx\u0000triage": 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/github-tracking-state.ts\u0000archived": 1,
|
|
"packages/dashboard/src/github-tracking-state.ts\u0000done": 1,
|
|
"packages/dashboard/src/reliability-metrics.ts\u0000done": 1,
|
|
"packages/dashboard/src/reliability-metrics.ts\u0000in-progress": 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/agent-heartbeat.ts\u0000archived": 1,
|
|
"packages/engine/src/agent-heartbeat.ts\u0000done": 1,
|
|
"packages/engine/src/cli-agent/task-session.ts\u0000done": 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/project-engine.ts\u0000in-review": 1,
|
|
"packages/engine/src/scheduler.ts\u0000archived": 1,
|
|
"packages/engine/src/scheduler.ts\u0000done": 1,
|
|
"packages/engine/src/triage.ts\u0000triage": 1,
|
|
"plugins/fusion-plugin-even-cards/src/cards/board-cards.ts\u0000archived": 1,
|
|
"plugins/fusion-plugin-even-cards/src/cards/board-cards.ts\u0000done": 1,
|
|
"plugins/fusion-plugin-even-realities-glasses/src/notifications/diff.ts\u0000done": 1,
|
|
"plugins/fusion-plugin-reports/src/store/report-types.ts\u0000archived": 1
|
|
},
|
|
"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
|
|
}
|
|
}
|