Files
fusion/scripts/lib/lifecycle-column-census-baseline.json
gsxdsm f9f06a4fb7 engine tail: nine files to zero — incl. a census-INVISIBLE requeue into a lane that does not exist (−13) (#2797)
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>
2026-07-30 11:28:54 -07:00

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
}
}