## Summary
Fusion can now ship on two release tracks. Betas are cut from `main` as
`vX.Y.Z-beta.N` (npm dist-tag `beta`, GitHub prerelease), stable
releases are promoted to a long-lived `release` branch and published to
`latest`, and users pick their track with the new `updateChannel` global
setting — via **Settings → General → Release channel** or `fn update
--channel <stable|beta>`. Previously everything was single-track: every
publish landed on `latest` and every update surface could only see it.
| | beta | stable |
|---|---|---|
| Cut from | `main` | `release` branch |
| Version | `X.Y.Z-beta.N` (changesets pre-mode) | `X.Y.Z` |
| npm dist-tag | `beta` | `latest` |
| GitHub Release | prerelease | latest |
| Homebrew tap / X draft | skipped | bumped / printed |
## How releasing works now
`pnpm release` prompts for the channel and **defaults to beta**, so
day-to-day releases are betas; stable is always an explicit choice.
Choosing stable from `main` triggers assisted promotion: the script
proposes the newest beta tag reachable from HEAD, verifies `release`
fast-forwards to it, then runs the whole stable release inside a
temporary git worktree on `release` — the primary checkout never leaves
`main`. Changesets pre-mode preserves changeset files across betas, so
the promoted stable release aggregates every changeset since the last
stable into one clean changelog entry.
## Design decisions
- **Every publish path names an explicit `--tag`.** A beta accidentally
landing on `latest` is the one unrecoverable failure of a dual-track
scheme, so nothing relies on npm's implicit default (`release.mjs`,
`version.yml`).
- **Beta channel resolves to semver-max of `latest` and `beta`**, so
beta users are offered each promoted stable once it overtakes their
prerelease. Switching beta → stable never downgrades; `fn update
--channel stable --force` is the explicit escape hatch.
- **One comparator instead of three.** CLI, dashboard, and desktop each
had their own `isRemoteNewer` that ignored prerelease identifiers —
`0.73.0-beta.2`, `-beta.3`, and `0.73.0` all compared equal, which
breaks the moment any beta exists. They now share full SemVer-precedence
helpers (`compareVersions`, `resolveUpdateTargetVersion`) from
`@fusion/core`.
- **Installs pin exact versions** (`@runfusion/fusion@0.73.0-beta.2`),
never a dist-tag, so an install can't silently land on the wrong track.
- **Desktop channels via electron-updater manifests.** Beta tags build
desktop artifacts with `publish.channel=beta` (emitting `beta*.yml`);
the app sets `channel`/`allowPrerelease` from the shared setting,
re-read on every manual check.
- **Update caches are channel-stamped** — a cache written for one
channel is never served to the other, so switching tracks takes effect
on the next check instead of after TTL.
## Test plan
- New unit coverage: SemVer precedence + channel resolution in
`@fusion/core` (30), channel behavior of the dashboard update check (28,
incl. 9 new) and `fn update` (16, incl. 8 new: persist `--channel`,
no-downgrade, `--force`, cache channel mismatch).
- `pnpm verify:fast` green (scoped typecheck, builds, CLI build, boot
smoke); desktop + settings-section suites green.
- `release.mjs` dry-run matrix exercised by hand: channel prompt
(default/override/invalid), branch preflights per channel,
assisted-promotion target selection, fast-forward guard against a
diverged `release` branch, and bootstrap when no `release` branch
exists.
- Not exercised live: an end-to-end publish (needs TTY authorization +
real npm publish). First real run is the first `pnpm release --channel
beta`.
---
[](https://github.com/EveryInc/compound-engineering-plugin)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added beta and stable release channels across CLI, dashboard, and
desktop updates.
* Users can select a channel via Settings or `fn update --channel
<stable|beta>` (stored as a global default).
* Desktop beta releases now generate beta update manifests and publish
as prereleases.
* **Documentation**
* Expanded release-track, settings, and CLI references to explain
channel semantics and workflows.
* **Bug Fixes**
* Updates now pin the resolved version per channel, improve version
comparison, and prevent unintended cross-channel downgrades unless
`--force` is used.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary
PostgreSQL-backed projects now record automatic TaskStore lifecycle
activity, including ordinary task creation and movement. Backend
initialization previously returned before registering the lifecycle
listeners, while features that called `recordActivity()` directly could
make the log appear only partially affected. The fix wires the
backend-agnostic listeners without changing SQLite initialization
behavior.
## Validation
- PostgreSQL activity parity test verifies all six listener surfaces and
persisted `task:created` and `task:moved` entries.
- `pnpm lint`
- `pnpm --filter @fusion/core typecheck`
- `pnpm check:changesets --strict`
- `pnpm verify:fast`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Restored automatic task lifecycle entries in PostgreSQL activity logs
by ensuring task lifecycle activity listeners are wired during
PostgreSQL backend initialization.
* Activity logs now reliably include task creation and movement events
(with transition details) immediately after backend startup.
* **Tests**
* Added a PostgreSQL activity-log parity test to verify listeners are
connected and that activity logs reflect `task:created` and `task:moved`
as expected.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Windows installs with slow native dependencies now get five minutes to
finish, and a real timeout is reported as an actionable terminal retry
instead of a wall of preceding npm deprecation warnings. Registry
`ETIMEDOUT` errors keep their network diagnosis, including after the
legacy-bin `--force` retry.
Compound Engineering personas are now included in the published CLI
bundle, with complete source-to-staged coverage for all persona
definitions and a clear startup error if the bundled assets are missing
or empty.
The PostgreSQL statement visible in the report was validated by the
existing real-Postgres schema reapply test. Its actual `caused by`
detail was truncated, so this PR deliberately makes no speculative
database change.
## Validation
- Dashboard updater tests: 22 passed
- CLI updater tests: 16 passed
- CE persona installer tests: 7 passed
- Published bundle persona assertion: passed against every source
persona
- CLI and CE plugin typechecks: passed
- Changed production/config lint and strict changeset validation: passed
- Real PostgreSQL schema reapply integration test: passed
---
[](https://github.com/EveryInc/compound-engineering-plugin)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Windows CLI and dashboard updates now allow up to five minutes for
installation and restore Compound Engineering agent personas during npm
installs.
* Update failures now surface clearer, terminal timeout guidance (while
preserving specific network connection diagnostics) and avoid misleading
“deprecated”/generic timeout text.
* Persona assets are reliably included in plugin builds and bunded
persona installation now errors clearly when definitions are missing or
empty.
* **Tests**
* Expanded update and bundling coverage for the new 5-minute timeout and
error-handling scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Completes the IR-driven lifecycle cutover: **the workflow IR becomes the
single source of truth for task lifecycle.** Node column assignments
move cards at runtime, every lifecycle predicate re-keys on column
traits instead of literal column ids, and the graph exclusively owns
review gates.
Plan (the spec for this work):
[`docs/plans/2026-07-18-001-refactor-ir-driven-lifecycle-cutover-plan.md`](docs/plans/2026-07-18-001-refactor-ir-driven-lifecycle-cutover-plan.md)
## What changed
**IR as runtime authority (R1, R2).** Graph traversal crossing a node
column boundary moves the card through the store's trait-hook `moveTask`
path, attributed `workflowMoveSource: "workflow-graph"` and emitting
`task:column-transition`. This replaces the executor's hardcoded
`moveTask(id, "in-review")` merge boundary and its handoff-invariant
allowlist. Scheduler, hold/release, self-healing, merger and
finalization now key on column traits
(`intake`/`hold`/`wip`/`merge-blocker`/`human-review`/`merge`/`complete`/`archived`/`timing`/`abort-on-exit`/`reset-on-entry`/`stall-detection`),
with rebound targets resolved per KTD-10.
**Single ownership of review gates (R4, R5).** Triage's out-of-graph
Plan Review gate is deleted; the graph is the sole author. `pending`
step results are CAS-claimed leases with owner and staleness floor
(KTD-4), so a crash/restart re-entry can no longer dispatch a second
reviewer and silently discard the losing verdict.
**Graph ownership is unconditional (R9).** The legacy execute fallback
is gone: `maybeExecuteWorkflowGraph` is now `executeWorkflowGraph`
returning `void`, `graphCompletion` is a required parameter, and a store
that cannot resolve a workflow fails closed rather than silently running
nothing. Also deleted, with a tombstone ratchet: `fn_review_step` and
its RETHINK/session-rewind machinery, `workflow-cutover.ts`,
`workflow-authoritative-driver.ts`, `workflow-parity-observer.ts`, and
the `graphCompletionInterceptors` map.
**`reviewLevel` becomes a creation-time preset (R6)** writing
`enabledWorkflowSteps`, with zero runtime reads.
**Upgrade path (R10).** Migration 0026 adds the durable per-node-entry
IR pin (KTD-3) and the one-time adoption stamp (KTD-8);
`planLegacyAdoption` is the single shared decision run by both the
startup sweep and the store-open reconcile, so pre-cutover rows are
adopted instead of freezing. A stale-binary guard refuses to open a
database migrated by a newer binary.
**Operator surfaces (R2, R11).** Four places still closed the column
set: the dashboard coerced every ingested task's column through the
legacy six-id enum (a card in a custom `Merging` column rendered in
**Triage**), `POST /tasks/:id/move` answered 400 for any
workflow-defined column, retry/reset/re-engage/unassign/spec-revise used
hardcoded move targets, and GitHub issue open/closed mapping
literal-compared `done`/`archived`. All now resolve from the task's
workflow by trait, each with a legacy fallback so `builtin:coding` is
byte-identical.
## Evidence
`builtin:coding` keeps its column ids and observable behavior
byte-compatible (R8, KTD-7), pinned by a characterization oracle. A new
**6-column benchmark acceptance suite** drives a user-authored workflow
— `Ideas → Todo → In-progress → In-review → Merging → Done` — asserting
the ordered transition trail, single-mover at the hold→wip seam (KTD-2),
column-role purity (R12), bounded review cycles from workflow config,
and park-in-place on failure (R3). The same fixture is proven
**editor-buildable** through the real save-validation path, plus
negative cases.
Verified locally on this branch, post-rebase:
- `pnpm test:gate` — green (engine-core 294/294, pg-gate 126/126,
ci-workflow 63/63)
- characterization oracle 59/59, tombstones 5/5, 6-column benchmark
11/11
- `tsc --noEmit` clean for `@fusion/core`, `@fusion/engine`,
`@fusion/dashboard` (both `tsconfig.json` and `tsconfig.app.json`)
## Known reds
- **`executor-task-done-invariant` → "moves a cleanly completed task to
in-review via the merge-node boundary"** — red on this branch. A
real-Postgres test whose graph re-entry rebounds the card to
`in-progress` after `execute()` returns. Not in the merge gate, so it
does not gate CI. Honest status: I could **not** verify it green on
pristine `main` — running main's tests in this worktree reuses built
artifacts and produced obviously polluted results, so I am not claiming
"pre-existing". It needs its own look.
- **`html2canvas` / FN-8309 — fixed here by deleting dead code.**
`packages/dashboard/app/utils/capture-screenshot.ts` imported
`html2canvas`, which is not a dependency of `@fusion/dashboard` and is
**not in `pnpm-lock.yaml` at all**, so it had never compiled in CI. The
file had **zero importers**. Main never caught it because PR Checks runs
only on pull requests (main's last PR Checks run was in June) while
main's own pushes run just the non-blocking Full Suite — so the required
**Typecheck** check was failing on *every* PR against main, including
this one. Inherited from `88b0db0f4` (FN-8309). **To restore when the
feature lands its dependency properly:** `git checkout 88b0db0f4 --
packages/dashboard/app/utils/capture-screenshot.ts` and add
`html2canvas` to `packages/dashboard/package.json` in the same change.
- **pg-gate rotating contention** — historically a different file set
each run with zero assertion failures. It passed 126/126 on the final
run here.
Two entries that were on the provisional ledger turned out **not** to be
pre-existing and are fixed in this PR: the
`workflow-graph-optional-step-fix` replan-cap pair were stale assertions
against U3's own contract change (cap-exhausted now *parks*
awaiting-approval and reports handled, rather than silently leaving the
task in place), and `executor-column-agent-seams` /
`executor-fast-mode-workflows` are green.
## Deferred follow-ups
- **Graph does not suspend at the ready-for-release seam.** A parked
`onNodeEntry` returns `void` and the node executes anyway, so within one
walk the card can run In-progress work while still displayed in Todo.
The benchmark models the scheduler explicitly for this reason and says
so at the seam. Making the graph actually suspend is U4-scope follow-up.
- **`needs-replan` reader migration.** Post-U3 the durable write happens
at the graph's own `plan-replan` seam, so the workflow *is* the writer
and the 14 readers form one coherent graph-owned loop — it is the
graph's durable replan signal wearing a legacy name, not un-migrated
legacy. The adoption census guard requiring that literal in
`executor.ts` is correct and stays. Migrating those readers to a
purpose-built run-state signal is a post-cutover naming change with its
own risk budget.
- **U9b seam-node refinement.** The merge substates
(`merging`/`merging-pr`/`merging-fix`) are adopted as `resume-graph`
rather than mapped to an exact re-entry node; naming a precise node
would require resolving the task's IR, which the adoption module
deliberately cannot do.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Workflows now drive task lifecycle columns, transitions, capacity
limits, review gates, and completion behavior—including custom
workflows.
* Tasks created with review levels automatically receive the
corresponding workflow review steps.
* Legacy in-progress tasks are automatically recovered during upgrades.
* Dashboard status badges now show the active workflow step name.
* Added safeguards for workflow changes, review ownership, database
compatibility, and workflow validation.
* **Bug Fixes**
* Fixed custom-column rendering and task movement.
* Improved merge-boundary handling and completion for workflows without
merge steps.
* Prevented cards from stalling, moving backward, or exceeding pooled
WIP capacity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Part **2 of 3** of the IR-driven lifecycle cutover (stacked on #2341;
top is #2335).
**Scope (80 files, packages/engine + cli/pi skill docs +
AGENTS/architecture):** graph-driven column moves via the
column-boundary controller (R1), single-mover scheduler/hold-release
trait cutover (KTD-2/KTD-9), trait re-keyed self-healing + merger with
the R7b confirmed-merge-must-finalize guarantee, graph-exclusive Plan
Review with leased dedup (R4/R5), the executeCore body-lift — zero
legacy re-entry — with fn_review_step + interceptor machinery deleted
and tombstone-ratcheted (R9), builtin workflow runtime fixes (missing
hold handler, unseamed-node column inheritance, no-merge completion
mover), the 6-column benchmark acceptance suite (11 tests) + 12-builtin
lifecycle sweep (94 assertions), and the executor test-harness
modernization. Also retires core's interpreter-cutover scaffolding whose
last consumer (the authoritative driver) dies here.
**Merge order:** #2341 → this → #2335. After #2341 merges, retarget this
to main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Part **1 of 3** of the IR-driven lifecycle cutover (split from #2335 to
fit review-tool file limits; plan:
docs/plans/2026-07-18-001-refactor-ir-driven-lifecycle-cutover-plan.md,
included here).
**Scope (48 files, packages/core + docs/plans):** shared transition
policy + validator (KTD-5), IR validation hardening incl. the benchmark
capability floor, CAS review leases (KTD-4), pooled WIP capacity budgets
(KTD-9), lifecycle-trait helpers, durable IR pin/drift detection
(KTD-3), review-level creation-time preset, legacy adoption module +
census + migration 0026 + stale-binary guard (KTD-8), core-side builtin
workflow fixes (single default-IR authority, no-merge complete-column
support).
Note: `workflow-cutover.ts` (interpreter parity scaffolding) stays alive
in this PR — its last consumer dies in part 2/3, which retires it.
**Merge order:** this PR → #TBD-2 (engine) → #2335 (dashboard/top).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added workflow-trait-driven task lifecycle transitions, including WIP
capacity pooling and workflow-aware recovery (IR pinning + drift
detection).
* Added legacy adoption/backfill for pre-cutover task states, with
unmappable rows safely parked.
* Added create-time `reviewLevel` presets to automatically configure
enabled workflow steps.
* **Bug Fixes**
* Fixed workflow moves when no workflow selection exists.
* Improved merge-blocker validation to be keyed to the workflow’s actual
review-lane identity, preventing invalid moves and misclassified
terminal states.
* **Tests**
* Added end-to-end and unit/integration coverage for workflow
validation, legacy adoption, migrations/schema guards, leases, review
presets, and transition rules.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
SQLite INTEGER is effectively int64, but the PostgreSQL baseline mapped
unbounded token/usage counters on `project.tasks` and
`project.chat_token_usage` to `integer` (int4). Real data contains
values > 2,147,483,647, causing the SQLite-to-PostgreSQL migration to
fail with `value ... is out of range for type integer`.
Changes:
- Change baseline DDL to `bigint` for the affected columns.
- Update Drizzle schema to `bigint({ mode: "number" })` to preserve JS
`number` semantics.
- Add forward migration `0024_bigint_counters.sql` for existing
clusters.
- Bump `SCHEMA_BASELINE_VERSION` to `0024`.
Fixes the int4 overflow observed during migration of large token/usage
counters.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Expanded token-usage and activity/lease counters to 64-bit integers to
prevent overflow on large workloads.
* Improved distributed task ID state/reservations to be isolated per
project and to merge/update conflicting entries more reliably.
* **Chores**
* Added an idempotent PostgreSQL migration for bigint counter support
and advanced schema baseline tracking.
* Updated dashboard build support by adding `html2canvas` type
definitions and the production dependency.
* **Tests**
* Updated schema-applier migration checks to include the new
bigint-counters baseline identity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
## Summary
Main Full Suite shards have been red after recent landings. Root causes:
1. **Executor tests** — `execute()` now polls
`getTaskVerificationRequestAsync` (chat-enqueued verification). Shared
`createMockStore()` (and soft-delete inline store) lacked the method, so
nearly every execute-path suite failed with `is not a function`.
2. **TaskDetailModal suites** — `NativeStructurePreview` imports `Map` /
`Lightbulb` / `BarChart3` / `Target` / `CircleAlert` from lucide; the
shared TaskDetail lucide mock omitted them, so suites failed at import.
3. **Grok process-lifecycle** — 15s bound stress timed out under
full-suite load without product-bug evidence → quarantined on sight per
AGENTS.md.
## Test plan
- [x] `executor-task-done-blocked`, `executor-fast-mode-workflows`,
concurrent-execute race
- [x] `executor-step-session`, plan-only scope leak, review-step
indexing
- [x] `TaskDetailModal.create-pr` + `TaskDetail.mobile-transition`
- [ ] Full Suite CI on this PR
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Improvements**
* Added `html2canvas` support in the dashboard to enable HTML-to-canvas
rendering needed for visual structure previews.
* **Tests**
* Updated task execution test mocks to handle task verification-request
flows reliably.
* Improved task deletion safeguard coverage and related execution
behavior checks.
* Enhanced test stubs to support structure preview rendering elements
during modal-related tests.
* **Chores**
* Quarantined a timing-sensitive process lifecycle test and refreshed
quarantine tracking to improve full-suite stability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Prevent automated tests from inheriting production PostgreSQL URLs and route global test-mode startups to a dedicated external or embedded test database.
Retry MCP bootstrap with bounded backoff and request deadlines, then continue with healthy servers when an integration remains unavailable.
Exclude definitions with unresolved secrets so graceful degradation never connects a partially authenticated server.
Serialize cross-parent diagnostic claims at project scope and reuse a recent active canonical task. Keep ordinary, completed, and path-distinct work independent.
Keep live workflow output at the tail only while the reader remains pinned.
- Track whether readers remain near the live log tail before following output.
- Observe in-place streamed content growth and resume following after re-pinning.
- Cover desktop and mobile scrolling behavior with workflow log regression tests.
Files changed:
.changeset/fn-8345-workflow-live-log-scroll.md | 7 +
.../app/components/WorkflowResultsTab.tsx | 113 ++++++++++------
.../__tests__/WorkflowResultsTab.test.tsx | 146 ++++++++++++++++++++-
3 files changed, 225 insertions(+), 41 deletions(-)
Fusion-Task-Id: FN-8345
Fusion-Task-Lineage: 44075099-738d-4c61-b482-dd7c500539d3
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Keep filtered mobile model dropdown lists scrollable when the virtual keyboard repositions the viewport.
- Allow the portaled list flex child to shrink and retain overflow ownership.
- Cover desktop and mobile scroll contracts, filtering, keyboard repositioning, and no-result states.
- Add a patch changeset for the mobile dropdown fix.
Files changed:
.changeset/fn-8355-mobile-model-dropdown-scroll.md | 7 ++
.../app/components/CustomModelDropdown.css | 4 ++
.../__tests__/CustomModelDropdown.test.tsx | 79 ++++++++++++++++++++++
3 files changed, 90 insertions(+)
Fusion-Task-Id: FN-8355
Fusion-Task-Lineage: 4e9dff76-f347-45ff-8ef6-abd7eea90d20
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Preserve the sticky-bottom follow invariant across streamed Dev Server and System Controls output.
- Track pinned-bottom state synchronously for rebuild and system-log streams.
- Follow in-place Dev Server transcript mutations only while the reader is pinned.
- Cover manual unsnapping, stream growth, and re-pinning behavior.
- Add a patch changeset for live-log scrolling.
Files changed:
.changeset/fn-8346-live-tail-scroll-follow.md | 7 ++
.../app/components/DevServerLogViewer.tsx | 43 ++++++++++--
.../__tests__/DevServerLogViewer.test.tsx | 47 +++++++++++++
.../__tests__/SystemControlsArea.test.tsx | 81 +++++++++++++++++++++-
.../command-center/areas/SystemControlsArea.tsx | 44 ++++++++++--
5 files changed, 208 insertions(+), 14 deletions(-)
Fusion-Task-Id: FN-8346
Fusion-Task-Lineage: c1eeb84d-c0af-473b-99f1-0d8564ac8a24
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
## Summary
Concurrent PostgreSQL project initialization no longer causes transient
dashboard failures, including repeated `GET /api/remote/status` 500
responses. The failure was a database deadlock between project-row
identity promotion and schema/plugin DDL, which previously acquired
overlapping locks in inconsistent orders.
This establishes one advisory-lock order across SQLite cutover, project
identity promotion, and schema mutations. Focused regression coverage
proves schema DDL waits behind an active migration transaction and that
identity stamping acquires the migration lock before reading
project-owned tables.
## Validation
- 25 focused unit tests passed.
- 3 focused real-PostgreSQL regression tests passed.
- `@fusion/core` typecheck passed.
- Strict changeset validation passed.
- Fast workspace verification passed, including the CLI build and boot
health check.
---
[](https://github.com/EveryInc/compound-engineering-plugin)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Prevented transient dashboard failures caused by PostgreSQL startup
and migration deadlocks.
* Improved serialization when multiple projects initialize or update
database schemas concurrently.
* Ensured migration state updates and schema changes occur in a
consistent order.
* **Tests**
* Added coverage for migration lock ordering, concurrent schema
operations, and recovery after lock contention.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Fixes a race where a stuck-kill during Plan Review could leave an
approved task stranded in `todo` as **unplanned**, so the scheduler
refused release to `in-progress` for several minutes (observed on
FN-1312: ~6m39s idle after Plan Review APPROVE).
### What went wrong
1. Finalize clears `status` early, then runs Plan Review.
2. Stuck-kill disposes the main triage session near the 30m processing
threshold.
3. Stale eviction only treated the main session as live, so the card
left `processing` while Plan Review / finalize was still running.
4. A second planner claimed `status: "planning"` and rewrote
`PROMPT.md`.
5. The first finalize moved `triage → todo` without clearing planning
statuses.
6. Hold-release saw planning/`needs-replan` and blocked: *“card is
unplanned and cannot enter processing column in-progress”*.
### Fix
In `packages/engine/src/triage.ts`:
- Track **finalizing** handoffs and **live Plan Review subagents** as
live planning work.
- Keep them in `getProcessingTaskIds`, refuse concurrent `specifyTask`,
and skip stale-processing eviction while they are live.
- **Defer** stuck-abort requeue during finalize (bump kill count only;
do not force `needs-replan`).
- Recover approved drafts with `status: null` (post early-clear), not
only `status: "planning"`.
- Re-assert `status: null` after the successful release move to todo.
### Tests
Regression coverage in `triage.test.ts` for eviction retention,
processing-id inclusion, null-status recovery, needs-replan
non-recovery, and deferred stuck-abort during finalize.
## Test plan
- [x] `pnpm --filter @fusion/engine exec vitest run
src/__tests__/triage.test.ts -t "evictStaleProcessing|specified triage
recovery|stuck|recoverApproved|markStuckAborted|defers stuck-abort"`
(related cases green)
- [ ] CI gate on this PR
- [ ] Optional: reproduce stuck-kill mid–Plan Review and confirm todo
releases without a second full replan
## Notes
Secondary report (stuck-kill immediately after all implementation steps
complete, then 16s auto-recovery) is **out of scope** here; existing
recovery already continued by skipping completed steps.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved recovery for approved tasks when Plan Review has completed,
including correct handling of `null`-status drafts.
* Avoided recovering unplanned seed drafts and tasks that should enter a
replanning flow.
* Refined stuck-abort requeue/cleanup to respect in-progress Plan Review
and finalize handoffs.
* Prevented stale-processing eviction from removing tasks while Plan
Review/subagent finalization is still active.
* Blocked new triage discovery and task specification when finalize/Plan
Review work is ongoing, preventing handoff disruption.
* Preserved durable task state during handoff completion
(approval/failure/replan outcomes).
* **Tests**
* Added expanded triage recovery and stuck-abort/stale-eviction
regression coverage.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->