- Add completion handoff limbo recovery state to task storage and audit events for bounded self-healing retries
- Requeue eligible in-review tasks stuck after "Task marked done by agent" with grace-period, active-task, and merge-blocker safeguards
- Isolate executor run mutation context per task to prevent cross-task runId attribution leaks in scope-leak and completion logs
- Add reliability and executor regression tests covering limbo recovery, run-context isolation, and updated persistence behavior
Fusion-Task-Id: FN-4999
- Add stale-branch reclaim guards for active executor sessions, recent execution starts, and worktrees with uncommitted changes
- Emit branch:stale-active-reclaim-deferred run-audit telemetry with deferral metadata for self-healing decisions
- Cover reclaim deferral and legitimate reclaim behavior with reliability interaction tests
- Document the new stale active branch deferral contract in AGENTS.md
- Add a patch changeset for the published CLI package
Fusion-Task-Id: FN-4949
Added a verification guard for steps 1-4 in project-engine with companion regression tests covering both the merge-error recovery path and a post-finalize noop scenario using real git fixtures.
Fusion-Task-Id: FN-4944
Fusion-Task-Lineage: ca8fca22-c732-42e4-aa8d-a82981d2f36e
Production failure shape:
Auto-recovery failed: branch conflict unrecoverable \u2014
Branch fusion/fn-4847 is already checked out at /.../deft-crane
(tip a881ccc86660, 24 stranded commits since 0b28388876).
Run branch recovery and explicitly choose whether to reclaim or
discard prior work.
The 24 stranded commits are cross-task contamination residue from the
FN-4781/FN-4804/FN-4814 worktree-race era \u2014 they are NOT FN-4847's work.
Previously this paused the task with pausedReason='branch-conflict-
unrecoverable' and the task got stuck forever waiting for human
adjudication.
User intent (FN-4847): 'just create a new branch and keep going and
discard the old one'. Implementation:
1. auto-recovery.ts:actionForMode \u2014 in 'deterministic-only' mode (the
default), branch-conflict-unrecoverable now returns 'retry' (was
'pause'). This routes the failure to the handler instead of pausing.
2. auto-recovery-handlers/branch-worktree.ts \u2014 'live-foreign' inspection
no longer emits irreducible-pause. Instead:
- Check FN-4811 active-session registry. If the foreign worktree is
bound to a live executor/merger session, do NOT force-remove it
(would yank the live agent's filesystem). Just requeue and let
downstream conflict-recovery handle it.
- Otherwise: force-delete the foreign worktree (--force) + prune git
worktree admin entries + force-delete the branch. Errors at each
step are best-effort and logged.
- Emit new audit event 'branch-worktree:foreign-branch-discarded'
with stranded-commit count, live-ownership flag, success flags.
- Requeue task to 'todo' with preserveProgress, clearing
branch+baseCommitSha.
3. run-audit.ts \u2014 register new DatabaseMutationType.
4. executor-worktree.test.ts \u2014 update the 'records recovery context'
test to assert the new retry+requeue contract (was asserting the old
pause-with-status-failed contract).
Verification:
- Targeted suite (4 files, 343 tests): pass.
- pnpm --filter @fusion/engine build: clean.
- pnpm lint: clean.
Fusion-Task-Id: FN-4847
Symptom found while investigating 'tasks are still struggling': every
in-review task hitting pre-merge deterministic verification failed with
[verification:bootstrap] bootstrap preamble failed (exit 2):
[test-bootstrap] FAILED: workspace dist artifact rebuild did not complete.
[test-bootstrap] command: pnpm --filter @fusion/engine build
Because pnpm --filter @fusion/engine build hit 17 TS errors from a prior
autonomous-agent refactor introducing a RemovalReason enum-like object
and two new audit event types. The bootstrap preamble is run by the
merger before every direct-merge verification, so a broken engine
typecheck blocked EVERY task from merging.
Fixes:
1. Duplicate RemovalReason re-export in worktree-pool.ts
Both and
were present for the same identifier,
producing TS2300 'Duplicate identifier'. RemovalReason is a const
object with derived type (typeof-keyof pattern), so a single value
export covers both kinds; the type-only re-export was redundant.
2. GitMutationType union missing the FN-4811 audit event types
merger.ts and worktree-backend.ts were emitting
'worktree:removal-refused-active-session' and
'worktree:removal-forced-over-active-session' audit events, but the
union in run-audit.ts didn't include them. Added both.
3. self-healing.test.ts vi.mock had wrong RemovalReason keys
The mock only exposed 5 keys (SelfHealing*) but production code
references HardCancel, Executor*, Merger*, PoolPrune, etc. Calls
like removeWorktree({ reason: RemovalReason.MergerPostMerge }) were
getting reason=undefined, producing confusing 'cannot remove
worktree: [vitest] No RemovalReason export is defined on mock'
error messages. Updated the mock to mirror the production const
exactly.
4. worktree-backend.test.ts removeWorktree calls missing required reason
The new contract makes reason: RemovalReason a required field on
removeWorktree's input. Five existing test cases were missing it;
added reason: RemovalReason.MergerCleanup to each.
5. integrity-warning-persisted-dedup.test.ts Settings cast
The test's makeStore helper cast a partial settings object to
Settings; TS rejected the narrowed type. Cast through unknown.
Verification:
- pnpm --filter @fusion/engine build: clean
- pnpm lint: clean
- pnpm build (full workspace): clean
- pnpm --filter @fusion/engine test: 5045 pass, 1 pre-existing
aiMergeTask real-git timeout flake, 1 skipped
With this fix, the verification bootstrap can complete and the merger
can finalize tasks again.
Fusion-Task-Id: FN-4811