Commit Graph

7090 Commits

Author SHA1 Message Date
Fusion (runfusion.ai)
2ca18e983a feat(FN-4813): complete Step 4 — wire handoff into scheduler and lease recovery
Fusion-Task-Id: FN-4813
Fusion-Task-Lineage: 846893a5-2afa-4817-8f64-8c444d2fd713
2026-05-16 17:31:09 -07:00
Fusion (runfusion.ai)
76ac6597cb fix(FN-4813): export handoff policy types from core barrel
Fusion-Task-Id: FN-4813
Fusion-Task-Lineage: 846893a5-2afa-4817-8f64-8c444d2fd713
2026-05-16 17:31:09 -07:00
Fusion (runfusion.ai)
2ff19bc9c9 feat(FN-4813): complete Step 3 — add owning-node handoff policy
Fusion-Task-Id: FN-4813
Fusion-Task-Lineage: 846893a5-2afa-4817-8f64-8c444d2fd713
2026-05-16 17:31:09 -07:00
Fusion (runfusion.ai)
735c8f413b feat(FN-4813): complete Step 2 — add distributed claim mutex tests
Fusion-Task-Id: FN-4813
Fusion-Task-Lineage: 846893a5-2afa-4817-8f64-8c444d2fd713
2026-05-16 17:31:09 -07:00
Fusion
86237c9a50 fix(FN-4811): unblock @fusion/engine typecheck so verification bootstrap can run
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
2026-05-16 17:23:15 -07:00
Fusion (runfusion.ai)
be9baad3bb fix(FN-4807): unblock engine build with typed liveness mocks
Fusion-Task-Id: FN-4807
Fusion-Task-Lineage: 3f03bd51-71c3-43c4-abe0-f636edeaf52b
2026-05-16 17:08:09 -07:00
Fusion (runfusion.ai)
52ab8a312f test(FN-4807): complete Step 4 — harden autostash suite timeout and cleanup
Fusion-Task-Id: FN-4807
Fusion-Task-Lineage: 3f03bd51-71c3-43c4-abe0-f636edeaf52b
2026-05-16 17:08:09 -07:00
Fusion (runfusion.ai)
a3ff539619 test(FN-4807): complete Step 3 — bound FN-3936 replay timeout
Fusion-Task-Id: FN-4807
Fusion-Task-Lineage: 3f03bd51-71c3-43c4-abe0-f636edeaf52b
2026-05-16 17:08:09 -07:00
Fusion (runfusion.ai)
8fd47b619f feat(FN-4811): complete Step 3 — update removeWorktree call sites
Fusion-Task-Id: FN-4811
Fusion-Task-Lineage: f3dac123-cb46-4d31-8a7e-dfc664bfdc5f
2026-05-16 16:55:04 -07:00
Fusion (runfusion.ai)
7a438f63ee fix(FN-4811): repair step-session registry wiring recursion
Fusion-Task-Id: FN-4811
Fusion-Task-Lineage: f3dac123-cb46-4d31-8a7e-dfc664bfdc5f
2026-05-16 16:55:03 -07:00
Fusion (runfusion.ai)
69d7b282cf feat(FN-4811): complete Step 1 — add active session registry wiring
Fusion-Task-Id: FN-4811
Fusion-Task-Lineage: f3dac123-cb46-4d31-8a7e-dfc664bfdc5f
2026-05-16 16:55:01 -07:00
Fusion
f4aa6d7b8b fix(FN-4811): persist done-task integrity warnings across engine restarts
The periodic self-healing sweep at
SelfHealingManager.reconcileDoneTaskIntegrity() emits a single
'Integrity warning: done-task finalize evidence is unproven (<reason>)' log
entry per task when the task is in 'done' but has no provable on-main
evidence. Dedup was via an in-memory finalizeUnprovenWarned Set per manager
instance, so every engine restart resurfaced the same warning on the next
sweep — significant noise on done tasks that legitimately lack evidence,
typically residue of FN-4811 contamination (FN-4771/FN-4778 in production).

Adds an optional MergeDetails.integrityWarning = { warnedAt, reason } field
and persists it on the first warning. Both warning sites in
reconcileDoneTaskIntegrity() (the unproven-and-still-mergeable branch and
the unproven-final branch) now consult the persisted record:

  - Same reason as persisted → skip re-emitting, just rehydrate the in-memory
    Set for in-process consistency.
  - Different reason → re-warn (so a *new* classification problem still
    surfaces) and update the persisted record.

Tests added under
packages/engine/src/__tests__/reliability-interactions/integrity-warning-persisted-dedup.test.ts
(real-git, 4 cases):

  - First sweep: emits warning + persists record.
  - Second sweep, same instance: in-memory Set dedupes (existing contract).
  - Fresh manager (simulated engine restart) + pre-persisted record:
    persisted dedup suppresses re-emission.
  - Fresh manager + persisted record with different reason: must re-warn
    and overwrite the persisted reason.

Full engine suite: 308 files, 5041 tests pass, 1 skipped. Lint clean.

Fusion-Task-Id: FN-4811
2026-05-16 16:51:43 -07:00
Fusion (runfusion.ai)
ba8e912aa8 test(FN-4809): complete Step 3 — cover merge attempt audit emission
Fusion-Task-Id: FN-4809
Fusion-Task-Lineage: bd632e33-b376-47de-ad99-da4610ae4690
2026-05-16 16:50:17 -07:00
Fusion (runfusion.ai)
42b8eebc8e feat(FN-4809): complete Step 3 — add changeset for merge attempt audit coverage
Fusion-Task-Id: FN-4809
Fusion-Task-Lineage: bd632e33-b376-47de-ad99-da4610ae4690
2026-05-16 16:50:16 -07:00
Fusion (runfusion.ai)
d898801ec3 test(FN-4809): complete Step 2 — cover merge attempt audit emission
Fusion-Task-Id: FN-4809
Fusion-Task-Lineage: bd632e33-b376-47de-ad99-da4610ae4690
2026-05-16 16:50:16 -07:00
Fusion (runfusion.ai)
df76612d78 feat(FN-4809): complete Step 1 — emit merge attempt audit events
Fusion-Task-Id: FN-4809
Fusion-Task-Lineage: bd632e33-b376-47de-ad99-da4610ae4690
2026-05-16 16:50:15 -07:00
Fusion (runfusion.ai)
4e5bcee14f feat(FN-4809): complete Step 2 — emit merge attempt audit events
Fusion-Task-Id: FN-4809
Fusion-Task-Lineage: bd632e33-b376-47de-ad99-da4610ae4690
2026-05-16 16:50:15 -07:00
Fusion
dcd6bf60f3 fix(FN-4811): recover from validation-failed remove + collapse broken FN-4806 nested branches
Two follow-ups stacked on the FN-4811 active-worktree liveness gate:

1. Stale conflict-path recovery (FN-4813 production failure)

   When 'git worktree remove --force' fails with 'fatal: validation failed,
   cannot remove working tree', the worktree directory is missing on disk
   and the git admin entry is stale. Without this recovery, every retry of
   tryCreateWorktree on a stale conflict path failed 3 times with
   'automatic cleanup failed', leaving tasks unable to create worktrees.

   cleanupConflictingWorktree now catches that specific error class, runs
   'git worktree prune' to drop the stale admin entry, best-effort deletes
   the branch, and returns success so the caller can proceed.

   Implementation note: the original attempt used existsSync(worktreePath)
   as a pre-check, but vitest's vi.clearAllMocks() can leave the existsSync
   mock returning undefined, causing the new branch to fire inside tests
   that didn't expect it and leading to worker OOM in
   executor-worktree.test.ts. The error-class-based catch is robust against
   mock state and matches the real production failure signal exactly.

2. Collapsed broken FN-4806 nested branches

   The previous FN-4806 refactor (commit 087b1a766) accidentally nested the
   genuine 'agent finished without calling fn_task_done after N retries'
   failure path INSIDE the silent-recovery branch, meaning ordinary
   failures were being silently requeued (no status=failed, no onError, no
   retry-budget burn) instead of being surfaced.

   Restored the clean two-branch structure:

     } else if (retryAbortedDueToReclaim) {
       // silent recovery (FN-4806)
     } else {
       // genuine no-fn_task_done exhaustion: mark failed, onError, burn budget
     }

   Also clears baseCommitSha on silent recovery (matches the parallel
   session-start-failure path's metadata clearing).

Tests:

  - Adds 'FN-4811 follow-up (FN-4813): recovers from validation failed'
    case to active-worktree-removal-liveness.test.ts (12 total cases).
  - executor-recovery.test.ts no-fn_task_done reclaim coverage now
    asserts baseCommitSha is cleared.
  - executor-recovery.test.ts 'does not mark task as failed when invalid
    transition error occurs on completion' regression fixed by restoring
    the failure-path branch.
  - executor-core.test.ts 'still enforces fn_task_done requirement in
    fast mode' restored.

Full engine suite: 307 files, 5037 tests pass, 1 skipped. Lint clean.

Fusion-Task-Id: FN-4811
2026-05-16 16:25:28 -07:00
gsxdsm
4180816e01 feat(FN-4805): merge fusion/fn-4805 2026-05-16 16:03:09 -07:00
gsxdsm
2f2fdd5e42 feat(FN-4803): merge fusion/fn-4803 2026-05-16 16:02:43 -07:00
Fusion (runfusion.ai)
7310642b68 test(FN-4806): add reclaim retry coverage and changeset
Fusion-Task-Id: FN-4806
Fusion-Task-Lineage: 4192226a-b24c-4681-b0fa-ce86f40fce64
2026-05-16 16:01:23 -07:00
Fusion (runfusion.ai)
087b1a7668 feat(FN-4806): complete Step 1 — split reclaim retry failure path
Fusion-Task-Id: FN-4806
Fusion-Task-Lineage: 4192226a-b24c-4681-b0fa-ce86f40fce64
2026-05-16 16:01:22 -07:00
Fusion
4c26aa6e91 fix(FN-4811): refuse to force-remove worktrees actively bound to live sessions
The executor's conflict-recovery paths (cleanupConflictingWorktree,
handleBranchConflict, and tryCreateWorktree's live-foreign/stale-resolved
branches) could force-remove a worktree even when it was currently bound
to an active executor session. This caused the FN-4781/FN-4804 cascade:

  - 'Execution blocked: assigned worktree path disappeared mid-task' as
    git deleted the live agent's filesystem out from under it
  - Two parallel runs for the same task alive simultaneously, with the
    second run started in a fresh worktree while the first was still
    holding the old session
  - Cross-task log attribution (an FN-4804 runContext writing to FN-4781)
  - Post-merge 'branch tip misbound but content found on main via trailer'
    rescues firing on every successful merge as the bookkeeping was
    corrupted mid-merge

Adds a hard liveness gate centralized in findActiveWorktreeOwner(), which
checks both the in-memory activeWorktrees map and the DB for non-done,
non-paused, in-progress tasks bound to the worktree. The gate fires at
two points:

  1. cleanupConflictingWorktree returns false (refuses removal) when an
     active owner is found, logging an FN-4811 refusal entry.
  2. handleBranchConflict short-circuits to 'sticky' BEFORE invoking
     inspectBranchConflict, because some inspection branches force-remove
     unconditionally.

When cleanup is refused, the live-foreign and stale-resolved branches in
tryCreateWorktree now FALL THROUGH to the suffix-rename path (rather
than returning null) so the requesting task can still proceed without
disturbing the live owner.

Tests:

  - New reliability-interactions backstop at
    src/__tests__/reliability-interactions/active-worktree-removal-liveness.test.ts
    covers findActiveWorktreeOwner (5 cases: in-memory match, requesting
    task excluded, DB-level match, paused exclusion, terminal-column
    exclusion, self-exclusion), cleanupConflictingWorktree gate (3 cases:
    in-memory refuse, DB refuse, no-owner proceed), and handleBranchConflict
    gate (2 cases: short-circuit + inspection-skipped, no-owner proceeds).
  - Updates existing executor-worktree.test.ts assertion that was
    documenting the bug behavior (force-removing active worktree) to match
    the new contract (refuses + falls through to suffix-rename).

Full engine suite: 307 files, 5035 tests pass.

Fusion-Task-Id: FN-4811
2026-05-16 15:36:07 -07:00
Fusion
3f8a5e6839 fix(FN-4806): silently recover when worktree/branch reclaimed mid-retry
The executor's no-fn_task_done retry loop has two reclaim signals
(retryAbortedDueToReclaim=true): (1) pre-retry liveness recheck where the
task DB shows worktree/branch was cleared, and (2) session-start failure
because the worktree path no longer exists. Both are engine self-heal
situations triggered by FN-4546 stale-active-branch reclaim, FN-4742
self-healing removals, or related housekeeping paths — the agent never
got a fair retry attempt.

Previously these surfaced as task status=failed with error
'Worktree/branch reclaimed during no-fn_task_done retry — requeueing',
fired onError, and burned the taskDoneRetryCount budget. Three legitimate
problems followed: tasks accumulated spurious failures in the UI, the
exhausted-budget branch escalated reclaimed tasks to in-review instead of
retrying, and the noise masked the underlying worktree-removal regression
(FN-4811).

Now the reclaim branch silently:
- clears stale worktree/branch metadata so the next pickup creates a fresh worktree
- requeues to todo with preserveProgress
- logs an informational 'engine self-heal, no failure' line
- does NOT set status=failed, does NOT bump taskDoneRetryCount, does NOT call onError

The genuine 'agent finished without calling fn_task_done after N retries'
exhaustion path (retryAbortedDueToReclaim=false) is unchanged.

Tests updated in
packages/engine/src/__tests__/reliability-interactions/executor-no-task-done-vs-worktree-reclaim.test.ts
to assert the new silent-recovery contract on all three reclaim paths.

Fusion-Task-Id: FN-4806
2026-05-16 15:16:05 -07:00
Fusion (runfusion.ai)
22dbc4951e test(FN-4788): update schema-version assertions to 83
Fusion-Task-Id: FN-4788
Fusion-Task-Lineage: 8de6c7f4-cf0f-4b1f-b664-4e8533b88919
2026-05-16 15:05:15 -07:00
Fusion (runfusion.ai)
9b7550a3f1 test(FN-4788): complete Step 4 — add secrets schema coverage
Fusion-Task-Id: FN-4788
Fusion-Task-Lineage: 8de6c7f4-cf0f-4b1f-b664-4e8533b88919
2026-05-16 15:05:15 -07:00
Fusion (runfusion.ai)
7f294614b5 feat(FN-4788): complete Step 2 — add central secrets_global schema migration
Fusion-Task-Id: FN-4788
Fusion-Task-Lineage: 8de6c7f4-cf0f-4b1f-b664-4e8533b88919
2026-05-16 15:05:15 -07:00
Fusion (runfusion.ai)
268eec50a9 feat(FN-4788): complete Step 1 — add project secrets schema migration
Fusion-Task-Id: FN-4788
Fusion-Task-Lineage: 8de6c7f4-cf0f-4b1f-b664-4e8533b88919
2026-05-16 15:05:15 -07:00
Fusion (runfusion.ai)
5bb15b1713 test(FN-4803): stabilize cli bundled plugin integration timeout
Fusion-Task-Id: FN-4803
Fusion-Task-Lineage: cbfa10fd-33a6-469c-9cb4-3a647bc47b84
2026-05-16 14:56:56 -07:00
Fusion (runfusion.ai)
246609a415 feat(FN-4803): complete Steps 4-5 — docs update and changeset
Fusion-Task-Id: FN-4803
Fusion-Task-Lineage: cbfa10fd-33a6-469c-9cb4-3a647bc47b84
2026-05-16 14:56:56 -07:00
Fusion (runfusion.ai)
5204177c2b test(FN-4803): complete Step 3 — cover github dedup toggle behavior
Fusion-Task-Id: FN-4803
Fusion-Task-Lineage: cbfa10fd-33a6-469c-9cb4-3a647bc47b84
2026-05-16 14:56:56 -07:00
Fusion (runfusion.ai)
371b1be7f7 feat(FN-4803): complete Step 1 — add dedup toggle in settings modal
Fusion-Task-Id: FN-4803
Fusion-Task-Lineage: cbfa10fd-33a6-469c-9cb4-3a647bc47b84
2026-05-16 14:56:56 -07:00
gsxdsm
f313000362 chore: ignore desktop packaging artifacts
Add dist-electron/ and common installer artifact patterns (*.dmg,
*.zip blockmaps, *.AppImage, etc.) to .gitignore so electron-builder
output never gets staged into commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 14:56:30 -07:00
Fusion (runfusion.ai)
bb3f204a7d test(FN-4787): fix dashboard loader duplicate mock declaration
Fusion-Task-Id: FN-4787
Fusion-Task-Lineage: 9f174f4f-d305-4779-ba0d-562f2eab064c
2026-05-16 14:23:14 -07:00
Fusion (runfusion.ai)
b57d3bcc0c test(FN-4787): regression tests for heartbeat identity snapshot
Fusion-Task-Id: FN-4787
Fusion-Task-Lineage: 9f174f4f-d305-4779-ba0d-562f2eab064c
2026-05-16 14:23:14 -07:00
Fusion (runfusion.ai)
7cdd27feae fix(FN-4787): disambiguate identity snapshot soul/instructions/memory states
Fusion-Task-Id: FN-4787
Fusion-Task-Lineage: 9f174f4f-d305-4779-ba0d-562f2eab064c
2026-05-16 14:23:14 -07:00
Fusion (runfusion.ai)
fea0b51915 feat(FN-4783): complete Step 8 — documentation and changeset
Fusion-Task-Id: FN-4783
Fusion-Task-Lineage: 7a02897b-e303-4947-86b2-3dd7a343f653
2026-05-16 13:46:43 -07:00
Fusion (runfusion.ai)
415cd6a111 test(FN-4783): stabilize workspace test lanes for verification
Fusion-Task-Id: FN-4783
Fusion-Task-Lineage: 7a02897b-e303-4947-86b2-3dd7a343f653
2026-05-16 13:46:43 -07:00
Fusion (runfusion.ai)
3c272cda63 feat(FN-4783): complete Step 6 — expose oauth expiry notification toggle
Fusion-Task-Id: FN-4783
Fusion-Task-Lineage: 7a02897b-e303-4947-86b2-3dd7a343f653
2026-05-16 13:46:42 -07:00
Fusion (runfusion.ai)
5e159368dc feat(FN-4783): complete Steps 4-5 — provider rendering and engine wiring
Fusion-Task-Id: FN-4783
Fusion-Task-Lineage: 7a02897b-e303-4947-86b2-3dd7a343f653
2026-05-16 13:46:42 -07:00
Fusion (runfusion.ai)
b48f8f1fac feat(FN-4783): complete Step 2 — add OAuth expiry monitor
Fusion-Task-Id: FN-4783
Fusion-Task-Lineage: 7a02897b-e303-4947-86b2-3dd7a343f653
2026-05-16 13:46:42 -07:00
Fusion (runfusion.ai)
f8c050741c feat(FN-4783): complete Step 1 — add oauth token expired event types
Fusion-Task-Id: FN-4783
Fusion-Task-Lineage: 7a02897b-e303-4947-86b2-3dd7a343f653
2026-05-16 13:46:42 -07:00
Fusion (runfusion.ai)
fc86c4f429 feat(FN-4784): complete Step 4 — add changeset
Fusion-Task-Id: FN-4784
Fusion-Task-Lineage: e11a4758-93ab-451e-8721-ae8995aaa70d
2026-05-16 13:35:31 -07:00
Fusion (runfusion.ai)
8b6b4cfeb1 test(FN-4784): complete Step 2 — codex precedence coverage
Fusion-Task-Id: FN-4784
Fusion-Task-Lineage: e11a4758-93ab-451e-8721-ae8995aaa70d
2026-05-16 13:35:31 -07:00
Fusion (runfusion.ai)
fd01843d40 feat(FN-4784): complete Step 1 — prefer Fusion Codex OAuth
Fusion-Task-Id: FN-4784
Fusion-Task-Lineage: e11a4758-93ab-451e-8721-ae8995aaa70d
2026-05-16 13:35:28 -07:00
Fusion (runfusion.ai)
424c61f4f7 feat(FN-4780): complete Step 7 — add changeset
Fusion-Task-Id: FN-4780
Fusion-Task-Lineage: 7f052539-9d15-4997-8135-21434f065d4a
2026-05-16 13:24:56 -07:00
Fusion (runfusion.ai)
f3901808ed feat(FN-4780): complete Step 5 — clear SWR caches on version update
Fusion-Task-Id: FN-4780
Fusion-Task-Lineage: 7f052539-9d15-4997-8135-21434f065d4a
2026-05-16 13:24:56 -07:00
Fusion (runfusion.ai)
508ab73486 feat(FN-4780): complete Step 4 — cache hydrate useTasks
Fusion-Task-Id: FN-4780
Fusion-Task-Lineage: 7f052539-9d15-4997-8135-21434f065d4a
2026-05-16 13:24:52 -07:00
Fusion (runfusion.ai)
b073f4ab27 feat(FN-4780): complete Step 3 — hydrate useCurrentProject from cache
Fusion-Task-Id: FN-4780
Fusion-Task-Lineage: 7f052539-9d15-4997-8135-21434f065d4a
2026-05-16 13:24:51 -07:00
Fusion (runfusion.ai)
432d2e5e37 feat(FN-4780): complete Step 2 — hydrate useProjects from cache
Fusion-Task-Id: FN-4780
Fusion-Task-Lineage: 7f052539-9d15-4997-8135-21434f065d4a
2026-05-16 13:24:51 -07:00