Added a diff volume gate to the merger that blocks or warns on large diffs before merge completes. The feature includes a new `merger-diff-volume-gate` module wired into the merger, configurable via project settings, with tests covering all gate paths. Also updated docs and the changeset for the `@r
Fusion-Task-Id: FN-4072
Adds direct merge commit routing to the merger, allowing non-squash merges to bypass the squash-audit path when configured. The feature includes new `mergeCommitStrategy` settings, updated dashboard UI, expanded merger lifecycle tests, and documentation.
Fusion-Task-Id: FN-4069
Implements an overlap guard for the merger that prevents concurrent merges of tasks with conflicting file changes, wired through a new overlap-aware fallback strategy and exposed via a dashboard setting. The feature includes a 406-line test suite for the overlap guard, a 203-line lifecycle integrati
Fusion-Task-Id: FN-4071
Adds a squash-merge audit gate that verifies duplicate-cherry-pick commits and file-overlap losses after any squash merge into main, restoring the post-squash audit step to the merge workflow with test coverage for both the audit logic and the broader merger lifecycle.
Fusion-Task-Id: FN-4067
Fusion-Task-Lineage: 593aa917-5640-495b-b1ae-80c3eaa09d01
Adds a global `thinkingLogEnabled` setting that gates AI thinking log persistence across the engine (executor, reviewer, merger, triage, step-session) and exposes the control in the dashboard Settings modal, with tests verifying settings parity and modal behavior.
Fusion-Task-Id: FN-4062
The merge implements post-merge model resolution, using the merger's own session model for executing post-merge prompt workflow steps instead of falling back to executor model defaults. Tests cover the resolution hierarchy, and documentation clarifies the precedence order for downstream consumers.
Fusion-Task-Id: FN-3905
Merged FN-3834 to add a no-op merge recovery flow to the merger, including a branch-ahead detector, short-circuit logic to exclude no-op merges from the mergeable sweep, and finalization of no-op review tasks — backed by substantial test coverage across merger and self-healing modules.
Fusion-Task-Id: FN-3834
Adds task lineage storage infrastructure (FN-3990 Step 2), introducing a dedicated `task-lineage.ts` module and related types to track task identity and association relationships, with corresponding database schema and store support plus tests and documentation for the lineage reconciliation process
Fusion-Task-Id: FN-3990
The merger gains a safeguard that automatically stashes work when a finalize-reset leaves orphaned records, preventing merge-state corruption. Core exports the new orphan record type, project-engine wires the safeguard, and documentation traces the provenance flow; tests were added for the recovery
Fusion-Task-Id: FN-3932
- Add merge target branch resolver contract to core types and exports
- Update task lifecycle completion path to resolve and propagate merge target branch
- Use resolved merge target in merger execution instead of stale/default branch assumptions
- Expand core and CLI tests for merge-target resolution behavior and add changeset for @runfusion/fusion
Fusion-Task-Id: FN-3217
- Add resolveMergerSessionModel helper to centralize merger model selection precedence
- Route merger session creation paths through the helper for commit, push/rebase, and autostash recovery flows
- Add comprehensive engine tests for merger model resolution and fallback behavior
- Document merger model hierarchy in settings reference and add a patch changeset for @runfusion/fusion
Fusion-Task-Id: FN-3892
Merged FN-3863 brings stash recovery to the dashboard via three coordinated steps: engine-side orphan stash surfacing API in `merger.ts`, dashboard API routes for stash recovery data, and a new `StashRecoveryView` component with mobile support and inspect-diff row actions, integrated into the header
Fusion-Task-Id: FN-3863
Merger now properly cleans up autostash git refs that survive past their useful lifetime, fixing race conditions during restore/cleanup and adding scheduled stale-sweep runs in the engine. The feature includes docs for the autostash lifecycle, new test coverage for the cleanup and sweep paths, and a
Fusion-Task-Id: FN-3827
This merge delivers chat rooms with room send routing and delete-room UI in the dashboard (FN-3899), including fixes for bundled plugin view imports and a release changeset. Supporting changes include a merger improvement that tightens scope-warning diff base when baseBranch is missing, companies.sh
Fusion-Task-Id: FN-3899
Mirrors dashboard `resolveDiffBase` display-recovery: when `baseBranch` is
absent, compute `merge-base(HEAD, main)` and prefer it over a stale
`baseCommitSha` only if it strictly descends that SHA. Pre-merge rebase
on legacy/imported tasks (no recorded baseBranch) was producing inflated
"N files changed outside declared File Scope" warnings — FN-3898 surfaced
17 ghost files for a 3-file change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The merge introduces the universal web fetch tool across reviewer/merger/triage agents (FN-3803, 4 commits) with docs and regression test, extracts the roadmap as a plugin scaffold in `plugins/fusion-plugin-reports` (FN-3166, FN-3165) while removing dashboard roadmap backend and associated tests, ad
Fusion-Task-Id: FN-3803
Fixes the merger to re-squash instead of refusing when a no-op merge verification occurs mid-merge, with regression tests added to prevent recurrence. Also replaces hardcoded color/spacing values in ProjectCard and ProjectOverview CSS with design tokens for theme consistency.
Fusion-Task-Id: FN-3773
Added comprehensive test coverage for the merger module (124 lines in `merger.test.ts`), covering the in-merge fix from this branch with assertions that verify the corrected behavior.
Fusion-Task-Id: FN-3312
Merges major roadmap plugin extraction (FN-3160/3161/3162), hardening the merger with autostash orphan cleanup and TOCTOU defenses (FN-3755/3756), adding shared state snapshots for mesh sync (FN-3451), shipping polling task notifications for the even realities plugin (FN-3743), defaulting non-epheme
Fusion-Task-Id: FN-3756
When `git stash apply` exits non-zero without producing conflict markers
(typical causes: untracked-overwrite, path missing at HEAD, index-conflict
that git refuses to mark), the previous code logged the bare exception
message and abandoned the stash. Operators couldn't distinguish failure
shapes without grepping runtime logs, and the dev's uncommitted work was
left to manual recovery even when an automated path would have worked.
Three additions on top of the conflict-marker path that already exists:
1. Capture stderr from the failing `git stash apply` (execAsync attaches
stderr/stdout to the rejection) and surface it in the task feed and
any subsequent failure messages, so the operator sees `error: untracked
working tree files would be overwritten by merge:` directly instead of
hunting it in mergerLog.
2. Layer 1 fallback: extract the patch via `git stash show -p --binary`
and try `git apply --3way`. This is more permissive than `stash apply`
for several common shapes (notably untracked overwrites — `--3way`
produces conflict markers we can route to the existing AI resolver
where stash apply just refuses).
3. Layer 2 fallback: if `--3way` also hard-fails and smartConflictResolution
is enabled, spawn `runAiAgentForAutostashHardFail` with the patch text +
git stderr. The agent reconstructs the developer's edits on top of HEAD
by editing files directly, then the orchestrator scans for residual
conflict markers before declaring success and dropping the stash.
The existing conflict-marker path remains unchanged — only the previously-
abandoned hard-fail branch gains recovery. Stash is dropped on success at
every layer; on every failure path the stash is preserved for manual
recovery and the failure cause is surfaced to the task feed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dropAutostashBySha resolved SHA→stash@{N} then ran git stash drop ${ref}
non-atomically. Any other process (interactive shell, parallel merger,
fix-agent) pushing a stash between resolve and drop shifted the index, so
we silently dropped the wrong entry while leaving ours behind. The task
log then claimed "Restored pre-merge autostash X cleanly" even though the
stash was still in the list — observed on FN-3558 (e81e922) and others.
Verify the ref still resolves to our SHA via git rev-parse before dropping;
on mismatch, re-resolve and retry up to 5x. Return success/failure so the
caller can record honest status to the task feed instead of unconditionally
logging "cleanly".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tasks were getting stuck in In Review with "verification fix succeeded but no
merge commit could be created" even though the merge commit was already on
main. Verification failures on attempt 1 were being swallowed by the smart-
conflict-resolution retry path, triggering attempt 2 with a stale baseline,
and the in-merge-fix finalizer would then fail its phantom-merge check.
- Propagate VerificationError out of executeMergeAttempt so the in-merge fix
runs once on attempt 1 with the correct preAttemptHeadSha baseline.
- In commitOrAmendMergeWithFixes, recognize "task already on HEAD" via the
Fusion-Task-Id trailer (line-anchored match) and treat the no-progress
finalize as success instead of tripping the guard.
- Add real-git regression test plus update merger.test.ts call counts to
reflect the (now correctly absent) attempt-2 AI agent.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Autostash entries from prior runs piled up indefinitely (50+ on a single
working tree) because every silent restore failure (apply hard-fails on
untracked-overwrite, transient git error, etc.) leaves a permanent stash
and the warn-only behavior trains developers to ignore the warnings.
- Add `sweepAutostashOrphans`, called at merge entry: classifies each
orphan by diffing its stashed paths against HEAD. If every path is
byte-identical to HEAD the dev's work has already landed, so drop the
stash automatically. Live orphans (real lost work) get loud warnings
on both the engine log and the task feed.
- Surface every restore terminal outcome (`restored`, `failed`,
`conflict-needs-manual` from each path) via `store.logEntry` so devs
can see in `fn task show` what happened to their stash without
grepping engine logs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P0 — parsePorcelainZ rename/copy handling
Git's -z porcelain emits `R <new>\0<old>\0` for renames (and
C for copies). The naive split-and-slice treated <old> as an
independent dirty path, which made runObservedDestructiveSyncOp
warn about phantom "cleared paths" whenever a rename was in
flight. Now we detect R/C status and skip the trailing entry.
P1 — race-rescue loop unstages between attempts
`git stash create` snapshots the index without clearing it, so
iteration 2's `git add -A` would re-stage atop iteration 1's
leftovers. Tree differences inside the loop then reflected stale
staging rather than genuine new writes. Added a `git reset` at
the top of each iteration so every attempt starts from a clean
index baseline.
P1 — writeActiveMergerStatus is now atomic
Switched from in-place writeFileSync to temp-file + renameSync.
POSIX guarantees rename atomicity on the same filesystem, so a
reader can no longer catch the file mid-flush and return a
false-negative "no merger active" advisory.
P2 — Step regex em-dash clarity
`[—\-:]` is functionally fine but obscures intent; switched to
`(?:—|-|:)` so the em-dash branch is obvious. Added a test case
for the em-dash separator.
New tests:
- parse-porcelain-z.test.ts (8 cases including renames + copies)
- em-dash case added to derive-subject-summary.test.ts
247/247 merger-suite tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The race-rescue loop was firing on every merger run because
`git add -A && git stash create` does not clean the working tree —
files stay dirty post-stash, so a subsequent `snapshotDirtyFiles` saw
the SAME paths the primary stash had just captured and stashed them
again, producing identical-tree race-rescue duplicates (visible in
git stash list as `fusion-merger-autostash:FN-XXXX:race-rescue-0`
sitting next to its identical `fusion-merger-autostash:FN-XXXX:`).
Fix: list the path set captured by the primary stash via
`git stash show --name-only`, and only rescue paths in the current
dirty snapshot that are NOT in that set — those are genuine
late-dirty writes from concurrent dev edits or interleaved ops.
Also drop any rescue whose tree-SHA exactly equals the primary,
as a defensive belt-and-braces.
Existing duplicate race-rescue stashes are harmless (identical
content to their primaries) and can be dropped manually.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- writeActiveMergerStatus: writes .git/.fusion-merger-active.json
(taskId, pid, hostname, startedAt) at merge entry, deleted in finally.
Not a lock — purely informational so dashboards / status lines /
pre-Edit hooks can warn devs that rootDir is volatile during the run.
readActiveMergerStatus(rootDir) is exported for consumers.
- runObservedDestructiveSyncOp: snapshot-before/after wrapper around
destructive rootDir ops that are *supposed* to preserve unrelated
working-tree edits. resetMergeWithWarn now uses it — any future
silent wipe of dirty paths surfaces as an actionable warning instead
of going unnoticed. Not applied to the autostash's own reset
--hard / clean -fd; those are intentionally destructive and already
protected by the race-rescue stash.
- Race-rescue stashes from stashUnrelatedRootDirChanges are now
attached to the AutostashHandle and surfaced via store.logEntry so
the recovery command lands on the task feed instead of only
mergerLog.warn.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- deriveDeterministicSubjectSummary now picks the lowest-numbered
`complete Step N` headline (or the oldest commit) instead of the most
recent commit, so trailing quality-gate revisions stop hijacking the
squash-merge subject (FN-3617 landed as "align mailbox modal css..."
when 4 of 5 commits were the actual Claude OAuth fix).
- AI subject + body system prompts in ai-summarize.ts now weight by
commit theme rather than file size, so a small token cleanup that
touches a large CSS file no longer dominates the summary.
- stashUnrelatedRootDirChanges adds a bounded re-snapshot loop after
the primary stash is persisted but before \`git reset --hard\`. Any
late-dirty paths (concurrent dev edits during a long merger run,
parallel merger runs racing on rootDir, late test/build artifacts)
get captured in labeled \`race-rescue-N\` stashes recoverable from
\`git stash list\`, instead of being wiped by the destructive reset.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two cuts to wasted work in the merge verification loop:
1. After the in-merge fix agent runs, fingerprint the working tree
(`git diff HEAD` + `git status --porcelain`, sha256). If the post-fix
fingerprint matches pre-fix and is non-empty, the agent didn't actually
change anything — re-running the same failing command can only yield
the same failure, so log and report the attempt as unsuccessful without
paying the test/build cost. Empty fingerprints (snapshot tooling failed)
fall through to the existing re-run path so we never silently swallow a
real fix.
2. Inside `syncDependenciesForMerge`, hash the active lockfile and compare
against `node_modules/.fusion-install-marker` (written after each
successful install). When they match, skip `pnpm install
--frozen-lockfile` even if `package.json` is staged. Covers the common
case where `package.json` changes but the lockfile doesn't, and
amortizes install across auto-recovery re-enqueues that hit the same
worktree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged branch removes the duplicate desktop "Todos" nav entry from the Header, syncs the `allowParallelExecution` runtime toggle into `AgentDetailView`, and updates the corresponding docs (agents.md, settings-reference.md, todo-view.md, dashboard-guide.md) to reflect the navigation change. Tests wer
Fusion-Task-Id: FN-3539
TriageProcessor.stop() previously only halted the polling loop, so
in-flight specify sessions and their reviewer subagents kept streaming
past shutdown. Extracted the existing global-pause teardown into
abortAndDisposeActiveSessions() and call it from stop() too.
aiMergeTask creates three sessions during a merge — autostash resolver,
in-merge verification fix agent, and pull-rebase conflict resolver — but
only the autostash one was registered via onSession. The other two are
now registered (with onSession threaded through pushToRemoteAfterMerge
into the rebase resolver chain), so ProjectEngine.stop() actually
disposes whichever merger session is running when shutdown lands.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When rootDir is the developer's primary checkout, the merger stashes
uncommitted edits before its hard resets and applies them back at the
end. Previously a pop conflict logged a single warning and silently
left the stash in place — a subsequent merge would push another
autostash on top, burying the first. Recent FN-3299 work was lost this
way and surfaced two side-by-side fusion-merger-autostash entries in
the local stash list.
Three changes:
- AI auto-resolve on apply conflict. The new
runAiAgentForAutostashConflict spawns the same createResolvedAgentSession
path as the in-merge fix-agent, instructs it to clear conflict markers
in place without committing, and verifies markers are gone post-run.
On verified success the stash is dropped; on any failure or remaining
markers the stash is left intact for manual recovery.
- Outcome surfaced via new MergeResult.autostash (AutostashOutcome)
field so dashboard / CLI / daemon can show developers whether their
work was reapplied cleanly, AI-resolved, or needs manual recovery.
- Deterministic stash identity. Replaced `git stash push` + label-grep
(which races against concurrent stashing tools) with `git stash create`
+ `git stash store`, capturing SHA atomically with snapshot creation
and using it for apply / drop. Untracked files captured via `git add
-A` before create; cleanup via `git reset --hard` + `git clean -fd`.
Also surfaces orphaned `fusion-merger-autostash:*` entries from prior
runs at merge entry, so they can no longer be silently buried.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The merge completes FN-3498 across three steps: adds ownership-aware done-task reconciliation to the merger, prevents branch-missing head SHA pollution during merge operations, and restores workspace typecheck compatibility. Core changes touch the merger (103 lines) and self-healing module (67 lines
Fusion-Task-Id: FN-3498
This merge adds three major features: an eval domain (`eval-store.ts`, `eval-types.ts`) with persistence schema for evaluation data; a plugin dashboard view registry with navigation integration for third-party dashboard extensions; and GitHub source metadata traceability that locks and enforces issu
Fusion-Task-Id: FN-3513
The merger issues several `git reset --hard` / `git reset --merge` and
forced-checkout calls against `rootDir` during merge attempts. When
`rootDir` is the developer's primary checkout (common for solo / single-host
setups), those resets silently discard any unrelated unstaged or untracked
changes — we burned dev work this way during FN-3329 (dashboard-tui edits
wiped mid-flight by an unrelated merge run).
Snapshot dirty paths at entry to `aiMergeTask`, stash them under a
recognizable label including the taskId (`-u` to capture untracked), and
pop them in a finally block on every exit path. On pop conflict we leave
the stash intact and log a recovery hint rather than dropping it. Best-
effort: a stash failure logs and proceeds with the old behavior so the
merge itself is never blocked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces blanket `git add -A` in `commitOrAmendMergeWithFixes` with an
explicit allowlist of (squash-staged ∪ fix-agent-modified) paths, so
unrelated dirty files in the project root no longer get swept into a
task's squash commit. The in-merge fix agent now snapshots the working
tree before/after its session to capture exactly which files it touched.
Hardens the git invocations the allowlist relies on:
- All `git add` and `git checkout --ours/--theirs` calls switched from
shell-interpolated `execAsync` to `execFile` array form, eliminating
path-injection surface and batching per-file spawns into one call.
- `snapshotDirtyFiles` adopts `git -z` NUL-delimited parsing so paths
with embedded spaces or specials are handled correctly.
- Long allowlist debug logs are truncated to 20 entries with an overflow
marker.
Refused-to-stage paths emit a warn naming each file so the user can
audit what was filtered.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cleans up lint and type errors in the engine by removing unused imports from executor and merger, and replacing `any` types with proper type annotations in verification-utils.
Fusion-Task-Id: FN-3345
The verification-fix agent prompt previously forbade modifying files
unrelated to the failure, which blocked the natural fix when
deterministic merge verification failed because of stale/missing
plugin `dist/` outputs in sibling workspace packages (e.g.
`Failed to resolve import "./cli-spawn.js"` from
`fusion-plugin-hermes-runtime/dist`).
- Add explicit guidance to detect stale-artifact failure signatures
and rebuild the affected package(s) before editing source.
- Allow the agent to fix pre-existing breakage on the base branch,
preferring the smallest change that makes verification green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merges FN-3008 to add a "fallback-used" notification system: the engine now emits events when AI model fallbacks are triggered, dispatches notifications via ntfy/webhook providers, surfaces a session banner in the dashboard, and exposes a settings toggle to enable or disable these alerts.
Fusion-Task-Id: FN-3008