ReportDetailPanel transitively calls useReportPreview, which fires fetch()
from api.ts. jsdom has no fetch, the promise rejects, and the catch handler's
setError triggers a React update after the test environment is torn down —
React then accesses window and the suite fails with ReferenceError. The
engine failures previously masked this by failing the shard before the
teardown race could surface. Mocking the api module makes the preview
resolve synchronously and keeps the suite clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Test shards 3 and 4 were silently failing on every open PR because vitest's
CLI parser was treating `--shard X/Y` as positional file filters whenever the
arg arrived after a `--` separator. Removing the `--` in ci-test-shard.mjs
restores per-shard slicing; verified locally that shard 1/4 and 2/4 now run
distinct subsets.
The two consistently-failing engine tests:
1. self-healing in-review-branch-rebind ambiguous case-variant detection:
dedup keyed on lowercase branch name collapsed two physically distinct
refs (allowed on Linux ext4) into one candidate, so the "applied" path
ran instead of "ambiguous-candidates". Dedup now keys on the resolved
SHA — macOS APFS still collapses (same ref, same SHA), Linux keeps both
(distinct SHAs) and the ambiguity skip path fires as designed.
2. worktree-acquisition resume-misbinding spy: the production
verifyResumeBranchNotMisbound returns early when `git merge-base HEAD main`
fails, which is exactly what happens on shallow checkouts. Bumping the
test-shards checkout to fetch-depth: 0 makes CI mirror the local git
state these engine tests rely on.
Also adds `push: branches: [main]` to PR Checks so regressions like this
(which slipped into v0.33.0 with no post-merge run) go red immediately
on landing instead of being discovered on the next PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add stale-session check to the isPaginationRequest branch in loadMessages.
If the user switches sessions while pagination is in progress, the old
session's messages and hasMoreMessages state should not overwrite the
newly active session's state.
Remove duplicate coreSetup from reports-dashboard project setupFiles.
With extends: true, coreSetup is already inherited from the root config.
Only dashboardSetup needs to be explicitly added at the project level.
Remove duplicate coreSetup from reports-dashboard project setupFiles.
With extends: true, coreSetup is already inherited from the root config.
Only dashboardSetup needs to be explicitly added at the project level.
Resolves CI failures where Node.js 24 caused ReferenceError: window is not defined
in non-dashboard tests that shared setupFiles with dashboard tests.
Follows the same pattern already used by fusion-plugin-roadmap: split into two
vitest projects with proper environment isolation:
- reports-dashboard: jsdom, includes src/dashboard/** tests + dashboard test-setup.ts
- reports-node: node, includes all other tests, excludes dashboard tests
No test changes — same 104 tests, same pass/fail state.
Resolves CI failures where Node.js 24 caused ReferenceError: window is not defined
in non-dashboard tests that shared setupFiles with dashboard tests.
Follows the same pattern already used by fusion-plugin-roadmap: split into two
vitest projects with proper environment isolation:
- reports-dashboard: jsdom, includes src/dashboard/** tests + dashboard test-setup.ts
- reports-node: node, includes all other tests, excludes dashboard tests
No test changes — same 104 tests, same pass/fail state.
Previously, getProjectContext awaited ensureEngine() which could block the
HTTP request handler for several seconds while a project engine initialised
(e.g. for newly-registered projects). This caused curl --max-time timeouts
and empty responses on the first request to a new project.
Change: replace await ensureEngine() with onProjectAccessed() (fire-and-forget).
- First request to an unstarted project uses getOrCreateProjectStore directly
(fast, reads from SQLite) — no blocking.
- Engine starts in background; subsequent requests use the running engine.
- Eliminates timing-dependent restart workarounds in init scripts.
Fixes: fn settings import followed by immediate API calls returning empty.
Adds support for project-only notification deep links in the dashboard hook, with comprehensive test coverage across all deep-link URL shapes and a changeset for release.
Fusion-Task-Id: FN-5583
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5583
Adds integration test coverage for the GitHub tracking delete flow in `github-tracking-delete.test.ts`.
Fusion-Task-Id: FN-5582
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5582
Added soft-delete reliability sweeps and guardrails to prevent blocker residue from persisting across delete operations, including column drift detection, deleted row sweep guards, and in-progress delete reconciliation, with comprehensive test coverage and documentation updates to the soft-delete ve
Fusion-Task-Id: FN-5566
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5566
Added GitHub tracking reconciliation to sync hidden and deleted archived tasks on engine startup, spanning a new reconcile task listing method in the core store, a reconciler pass in the dashboard, and comprehensive test coverage for both the store listing and the deleted-archived reconciliation log
Fusion-Task-Id: FN-5577
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5577
The Git Manager's recent integration-advances list derived `needsAction`
purely from the original `merge:auto-sync` audit-event outcome, so it kept
showing "(N need action)" after the operator clicked "Sync working tree" or
fixed up the worktree by hand. `collectRecentMergeAdvances` now also checks
whether each advance's `toSha` is reachable from HEAD — if it is, the
worktree already contains that advance and `needsAction` is false
regardless of what the audit trail recorded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the allow-resurrection toggle for task deletion, letting users prevent deleted tasks from being automatically restored. Changes span the `ConfirmDialog` component, `TaskDetailModal`, and the `useConfirm` hook, with comprehensive test coverage across the dashboard API and UI layers.
Fusion-Task-Id: FN-5475
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5475
Three coupled fixes to make `pnpm test:full` exit cleanly when the local
`fn` dashboard is running:
1. scripts/check-test-isolation.mjs — replace timing-based "is the
engine writing?" heuristic with a deterministic check: if
`.fusion/engine.lock.lock/` exists (proper-lockfile's held-lock
marker), the dir is engine-active and auto-skipped from violation
reporting. The 2-second mutability probe is retained as a backstop
for dirs with another external writer but no live lock. Also adds
`engine.lock` / `engine.lock.lock/` to RUNTIME_IGNORE_PATTERNS so
a mid-test engine start/stop doesn't trip the signature compare.
2. packages/dashboard/.../__tests__/GitManagerModal.test.tsx — prune
the Status-panel Sync button + Recent-advances-events describe
blocks. Their UI was removed in 5d35b64bd ("remove duplicate
integration-advances UI") but the tests stayed and were timing
out at 1s each. The Remotes-panel Sync describe is kept because
the `remotes-sync-integration-tip-btn` still exists.
3. packages/engine/.../merge-reuse-task-worktree.slow.test.ts —
update the happy-path assertion to reflect 4c31e885b
("merger auto-syncs project-root checkout after ref advance").
Before that change, the merger's `update-ref` advance left the
project root's working tree stale, so `git status --porcelain`
would differ after the merge. With auto-sync, the new file is
tracked + clean at HEAD, so status doesn't change. Verify the
file actually landed via `git ls-files` instead.
After this, `pnpm test:full` exits 0 with the local dashboard running.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the squash commit was built off a stale integration tip, the FF guard
in advanceIntegrationBranchRef refused the swap with reason
`non-fast-forward-advance` — but the caller only mapped `concurrent-advance`
to IntegrationBranchConcurrentAdvanceError, so the non-FF case fell through
as a plain Error and failed the task. Both reasons share a root cause
(integration moved during the merge window), so they now share the
FN-4500/FN-5083 rebind/retry path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a static pretest check and a runtime vitest-setup wrapper that block
shell/process calls matching `kill|pkill|killall|fuser|lsof ... <port>` or
`.listen(<port>)` against reserved Fusion ports. Reserved set is dynamic:
default 4040 plus $PORT, $FUSION_SERVER_PORT, $FUSION_RESERVED_PORTS, and any
port responding to /api/health on 4040..4045 at worker startup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop a fixture-existence test that read PROMPT.md from an absolute
/Users/eclipxe path (CI would fail), and generalize remaining hardcoded
home-directory paths in self-healing and worktree-stale-registration
fixture strings to neutral /tmp/test-project and /repo paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two cleanups in Git Manager → Status:
- Removed the duplicate "Sync local tip" button (gm-integration-actions)
and the second "Recent integration advances" list (gm-recent-advances)
that rendered above the highlighted block. Also dropped the dead
mergeAdvanceEvents state, fetcher, and SSE subscription that only
fed the deleted UI.
- Sync working tree is now pure-local. Added skipOriginFetch to
PullGitBranchOptions.integration (and the matching POST /api/git/pull
body field). When set, pullGitBranch skips tryFastForwardFromOrigin
entirely — the sequence is just auto-stash → git reset --hard
refs/heads/<integration> → restore stash. The Sync button passes
skipOriginFetch: true because the "N need action" recovery is for
catching the worktree up to a *local* merger ref-advance; touching
origin could silently pull in unrelated remote commits.
Help disclosure rewritten to reflect the pure-local behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`notice` is `events.find(...)` which returns `undefined` (not `null`)
when no match. `waitFor(() => expect(...).not.toBeNull())` exited
immediately because `undefined !== null` — the test never actually
waited for the api mock to resolve. Sometimes the followup assertions
happened to land after the events fetched (test passed by luck);
sometimes they ran while notice was still undefined and the assertions
failed.
Switched all five waitFor sites to `.toBeDefined()` so they actually
block on the events-fetch resolution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the merger advances local `refs/heads/<integrationBranch>` via
`update-ref` without pushing, the user's project-root worktree HEAD
(symbolic to that branch) follows immediately to the new sha, but the
working files and index don't. The integration-mode pull only ran
`git merge --ff-only origin/<branch>`, which short-circuits as
"already up to date" when local is ahead of origin — leaving the
worktree visibly stale even though "Pull completed" was reported.
Pull now explicitly `git reset --hard <localIntegrationTip>` after
the origin fast-forward step. The autostash above protects user edits,
so the reset is safe regardless of whether the origin FF ran.
Regression test in routes-git.test.ts simulates the
local-ahead-of-origin scenario and asserts the reset-to-local-tip is
issued.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a fast-path method for scopes-related settings calls in the memory routes handler, replacing the previous implementation with a more efficient approach; tests are updated to cover the new route behavior.
Fusion-Task-Id: FN-5563
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5563
The gh CLI does not support 'htmlUrl' as a field name in
search repos --json output. It only supports 'url'.
This caused all research pipeline runs to fail silently because
GitHubProvider.search() was querying for 'htmlUrl' which gh returned
as undefined, leading to failed lookups.
Fix:
- Changed --json query parameters from 'htmlUrl' to 'url'
- Updated GitHubRepoResult and GitHubIssueResult types
- Updated all code references from repo.htmlUrl/issue.htmlUrl to repo.url/issue.url
- Updated test mocks accordingly
Fixes: DT-217, DT-218
Third root-cause fix in the FN-5475 sweep. When `aiMergeTask` /
`recoverNoOpReviewTasks` classified a task as `proven-no-op` or
`no-changes-finalized`, both call sites moved the task to Done while
clearing `modifiedFiles: []` — silently destroying the audit trail when the
work product was uncommitted in the worktree, squashed against the wrong
branch, or dropped by reuse-handoff churn. This was the load-bearing site
of the FN-5490 / FN-5517 / FN-5526 / FN-5540 lost-work patterns.
Both call sites now check `task.modifiedFiles.length` before finalizing as
no-op. If the task claims work was done but no commit landed, the task is
moved back to `todo` with progress preserved and a new
`task:finalize-lost-work-blocked` audit event is emitted. The next
executor run re-attempts the work; the operator sees the audit event in
the timeline.
The post-hoc `reconcileDoneTaskIntegrity` path is intentionally NOT gated
— it cleans up already-Done tasks (legacy state) and is out-of-scope for
prevention. 9 lost-work tasks already in this state at sweep time are
cataloged in docs/incidents/2026-05-23-lost-work-tasks.md for fresh
re-spec.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Settings inheritance (high):
- Restored the value !== initialProjectValue gate on the non-model
project branch. Previously every effective/inherited project key was
persisted as an explicit override on every save.
Git Manager UI lie in remote-only mode:
- "Local <branch> vs origin" card now renders "no local tracking"
instead of a green "Synced" badge when integrationTipSource ===
"remote-only" (no local branch to compare).
- New dedicated "HEAD vs origin/<branch>" card surfaces a meaningful
distance in remote-only mode.
isIndexStale correctness:
- Walks up to 16 reflog entries so multi-hop misses (A→B→C without
sync) are detected; the prior check only consulted @{1}.
- Gated on isOnIntegrationBranch === true so a feature-branch worktree
whose HEAD happens to descend from <integration>@{1} no longer trips
the FN-INDEX-DESYNC warning.
Enumeration-failed events surfaced:
- collectRecentMergeAdvances pairs events with (taskId, newSha) when
both are present, falls back to taskId-only for early-failure events
(e.g. "enumeration-failed") that have neither path nor newSha. The
diagnostic outcome now surfaces on the matching advance instead of
being silently dropped.
aheadOfIntegration semantics no longer shift:
- Split into three distinct fields: aheadOfIntegration (HEAD vs local),
aheadOfIntegrationRemote (HEAD vs origin/<branch>),
aheadOfOriginIntegration (local vs origin). Consumers no longer have
to read integrationTipSource to know which comparison they got.
currentBranch failure no longer masks wrong-branch state:
- Distinguish "command threw" (transient git error) from "command
succeeded with empty stdout" (legitimate detached HEAD). New
currentBranchDetectionFailed field lets the UI surface "branch
detection unavailable" on a real failure.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two root-cause fixes for the "fake done" patterns surfaced while debugging
FN-5475's stuck preflight (it depended on FN-5233, which the board reported
as Done but whose squash had stranded on a sibling fusion/fn-* branch).
1. resolveTaskMergeTarget rejects fusion/fn-* sibling branches as a merge
destination — when a task's baseBranch was inherited from a sibling/dependent
dispatch, the merger detached onto and squashed against that branch instead
of advancing main. New audit event surfaces the steering miss so the
underlying baseBranch-propagation bug stays observable.
2. self-healing findLandedTaskCommit verifies ownership against each grep
candidate's body before attribution. The previous code blindly accepted the
first hit of `git log --grep=FN-XXXX` (which matches the entire commit
message); FN-5441 and FN-5446 were both marked done against an unrelated
FN-5483 commit whose body merely mentioned them in prose. commitOwnedByTask
is also tightened: trailers must be line-anchored and the subject fallback
must match conventional-commit form, not a bare substring.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Settings (data-loss):
- Non-model project keys now use null-as-delete instead of dropping
undefined via JSON.stringify, so clearing Integration branch (Use
dropdown / auto-detect) actually clears the persisted value.
isIndexStale (false-positive AND false-negative):
- Replaced the empty-worktree heuristic with a reflog-anchored check:
stale iff refs/heads/<integration>@{1} exists, HEAD is descendant of
it, and `git diff-index --cached <prevTip>` is empty.
Auto-sync attribution in collectRecentMergeAdvances:
- Match auto-sync events by (taskId, newSha) instead of taskId-only;
re-merged tasks no longer have older advances mislabeled with the
newest outcome.
- Compare worktreePath after realpathSync on both sides; macOS symlink
paths no longer cause permanent "needs action" false positives.
Extended path no longer 500s:
- Route wraps computeExtendedGitStatus in try/catch and falls back to
basic status on failure. Inner `branch --show-current` wrapped too
so detached HEAD / non-git rootDir doesn't throw.
Integration branch falls back to remote-only ref:
- When refs/heads/<branch> is missing, use refs/remotes/origin/<branch>
as the integration tip. New `integrationTipSource` field
("local"|"remote-only"|"missing") drives a UI badge.
Copy commit hash:
- Short-SHA copy is the default and matches what's displayed; a
separate "full" button copies the 40-char headSha. Previously the
single button silently copied the full SHA when extended was on.
Detached HEAD:
- isOnIntegrationBranch left undefined when currentBranch is empty so
the UI doesn't render "(not on <integration>)" against a
no-branch state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the FN-5233 tombstone system for soft-delete resurrection: a configurable `tombstoneWindowSeconds` deduplicates recreation of recently deleted tasks, with an `allowResurrection` flag that permits explicit resurrect-on-recreate, tombstone recreate guards in the store layer, and cleanup of
Fusion-Task-Id: FN-5233
Replaces the plain text input with a <select> that lists the project's
local branches (loaded via fetchGitBranches when the Merge section
becomes visible) plus an (auto-detect) default and a Custom… option for
branches that don't exist locally yet.
Branch list is deduped + sorted with common integration names (main,
master, trunk, develop) pinned to the top so the typical case is one
click. Choosing Custom… swaps in a text input with a "Use dropdown"
link to revert.
A previously-saved value not in the loaded list (branch deleted
locally, or initial render before fetch resolves) falls through to the
custom input automatically so the operator can still see + edit it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Repository Status panel now answers "what is the actual state of my
project root vs the integration branch?" so operators can be sure of
the picture even when the Merge Advance Notice banner is dismissed.
GET /api/git/status accepts ?extended=1 and returns additional optional
fields:
- integrationBranch + integrationBranchSource (settings|origin-head|fallback)
- integrationTipSha / originIntegrationTipSha
- aheadOfIntegration / behindIntegration (HEAD vs local integration tip)
- aheadOfOriginIntegration / behindOriginIntegration (local tip vs origin)
- dirtyDetails {staged, modified, untracked, conflicted, sample}
- indexStaleVsHead (surfaces the FN-INDEX-DESYNC scenario)
- stashCount
- recentMergeAdvances: up to 5 merge:integration-ref-advance events
joined with merge:auto-sync outcomes; needsAction flag flips when
auto-sync didn't successfully bring this worktree forward
GitManagerModal renders all of it:
- Existing cards get sub-text: branch shows "not on <integration>",
Working Tree shows staged/modified/untracked/conflicted breakdown
- Second row: Integration branch + source, HEAD-vs-integration,
local-vs-origin, stash count
- Yellow warning panel when indexStaleVsHead surfaces the merger's
stale-index situation with a recovery hint
- Recent integration-branch advances list, color-coded by needsAction,
shows the per-advance auto-sync outcome so operators can audit
even after dismissing the banner
All fetchGitStatus calls in GitManagerModal switched to extended:true.
Other callers unaffected — extra fields are optional.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a text input for the canonical integration branch directly under
the Auto-completion mode select. Visible regardless of direct vs PR
mode since the setting applies to both.
Blank preserves the existing auto-resolution cascade
(integrationBranch → baseBranch → origin/HEAD → main). Setting it to
master/trunk/develop/etc. pins the resolution explicitly.
Field trims whitespace and stores undefined when cleared so the
auto-resolution stays active.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bugs were keeping the Merge Advance Notice banner stuck on screen
even when there was nothing for the user to do:
- Dismiss was dead: the `notice` memo never applied dismissedShas, so
clicking close (or a successful Pull, which calls dismiss()) updated
localStorage but the filter immediately re-matched the same event.
- Auto-sync success was ignored: with mergeAdvanceAutoSync defaulting
to "stash-and-ff", the merger snaps the project-root checkout
forward as part of the merge — nothing left to pull — but the banner
kept appearing. Clicking Pull then hit /api/git/pull which fetched
origin (no change, the merger only advanced the local ref) and
returned pull-clean with no real work done.
The notice memo now (a) filters dismissedShas, and (b) suppresses any
advance event whose autoSync entry for the current user's worktreePath
reports clean-sync or synced-with-edits-restored. Conflict + skipped
outcomes still surface so the user can recover.
Tests: dismiss removes the banner; clean-sync suppresses; pop-conflict
still surfaces; sibling-worktree success doesn't suppress this user.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the missing form control for the auto-sync mode introduced by the
merger hook. Lives next to Direct merge commit routing / Integration
worktree inside the merge-strategy panel and only renders when
mergeStrategy === "direct". Three options with descriptions cribbed from
docs/settings-reference.md:
- Stash + fast-forward (default): preserve local edits across the snap
- Fast-forward only: skip dirty worktrees, surface the banner instead
- Off: legacy behavior; project root stays stale until manual pull
Value is normalized through normalizeMergeAdvanceAutoSyncMode on both the
merged-settings and scoped-settings load paths so a missing/invalid
stored value cleanly falls back to the default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Origin's commit 9463be1a9 fixed the heartbeat-executor test by dropping the
over-tight `sourceRunId: undefined` key that was failing against the real
"run-001" after FN-5060 dedup. Local history converged on the same problem
via a broader fix (`sourceRunId: "run-001"` + sourceParentTaskId +
sourceMetadata.contentFingerprint asserted explicitly). Conflict resolved
in favor of HEAD's broader assertion since it both unblocks the failing
case and locks in the full FN-5060 source shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Data-loss fixes in syncWorktreeToHead:
- Untracked-restore checks `git ls-tree -r --name-only HEAD` to skip
paths the new tip added as tracked files; user bytes stay in the
stage dir instead of clobbering merged content.
- Apply-failure on a deleted/renamed file: conflictedFiles falls back
to parsing `diff --git a/<p> b/<p>` headers when --diff-filter=U
returns nothing.
- All git invocations pass `-c core.quotePath=false` so non-ASCII
paths round-trip through copyFileSync.
- Stash-and-ff re-verifies rev-parse HEAD === newSha right before
each `reset --hard HEAD` (TOCTOU). On mismatch we bail with patch
preserved on disk.
- Stage dir lifecycle moved into try/finally with preserveStageDir
flag — kept whenever the user's edits live only in patchPath; rm'd
on all clean exits.
- Patch written to disk before the apply attempt, not only on
failure, so a crash between snapshot and apply doesn't lose edits.
Multi-worktree-same-branch fix:
- New getRegisteredWorktreeBranches returns Array<{branch,path}>
instead of collapsing into a Map. Multiple worktrees can share a
branch via `git worktree add --force -b`; merger now syncs all of
them rather than silently skipping all but the last.
Contract + surfacing fixes:
- JSDoc on merge:auto-sync GitMutationType now lists the actually-
emitted outcome strings + stage enum.
- GET /api/tasks/merge-advance-events joins merge:auto-sync events
within ±5min of the advance and returns them in a new
`autoSync: AutoSyncOutcome[]` field; useMergeAdvanceNotice exposes
the same shape so the banner can surface pop-conflicts (including
patchPath) instead of dropping them.
Hygiene:
- Merger now reads the setting via normalizeMergeAdvanceAutoSyncMode
instead of an inline check + `as unknown` cast.
New tests:
- Untracked-collides-with-tracked preserves merged content.
- Apply failure on deleted file populates conflictedFiles from
patch header.
- Route surfaces autoSync outcomes (clean-sync + pop-conflict)
joined within the time window.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After advanceIntegrationBranchRef ff-updates refs/heads/<integrationBranch>,
the merger now enumerates other worktrees on that branch and reconciles
each one's index + working tree to the new tip via syncWorktreeToHead.
Not a git pull — origin may still be at the previous tip without
pushAfterMerge, so pull --ff-only is a no-op and a naive stash/pull/pop
ends with the worktree restored to the old state. Instead the new
worktree-ref-sync helper:
1. Diffs the worktree against the previous tip to isolate real edits
from the stale-index "phantom diff" against the new HEAD.
2. Snaps clean worktrees forward via reset --hard HEAD.
3. In stash-and-ff mode with real edits, captures them as a binary patch
against the previous tip, snaps to HEAD, then git apply --3way to
restore. Untracked files are saved + restored separately. Patch
conflicts surface as synced-with-pop-conflict with the patch left on
disk for manual recovery.
Per-worktree outcome emitted as merge:auto-sync (new GitMutationType).
Per-step pull:fast-forward / stash:push / stash:pop / stash:pop-conflict
that pass through the auditor are tagged metadata.autoSync=true.
Isolated in its own try-catch so an auto-sync failure can't fail the
already-landed merge. Default behavior is mergeAdvanceAutoSync="stash-and-ff";
"off" preserves the legacy surprise behavior.
Backstopped by merger-auto-sync.slow.test.ts: clean-sync snaps both index
and files forward, ff-only with real edits is a no-op, stash-and-ff
preserves untracked locals across the snap, task worktrees on fusion/fn-*
are skipped, empty branch map emits nothing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Standalone implementation of the stash → ff → pop pipeline used by the
upcoming mergeAdvanceAutoSync merger hook. Returns a discriminated union
(clean-pull | stash-pull-pop | stash-pop-conflict | skipped-dirty |
skipped-not-on-branch | failed) and emits structured audit events via an
optional callback. The dashboard's user-triggered Pull keeps using the
existing /api/git/pull integration path; smartPull stays free of AI
conflict resolution so the merger's post-advance auto-sync is safe to run
inline without escalating to a model call.
Backstopped by smart-pull.slow.test.ts (engine-slow lane): clean-pull,
stash-pull-pop, ff-only skip, off-branch skip, audit-emitter exception
tolerance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Schema for what the merger should do in other worktrees still checked out
on the integration branch when it advances the branch ref. Modes:
off — legacy (user pulls manually)
ff-only — fast-forward only when other worktree is clean
stash-and-ff — Smart Pull pipeline (default)
Threads through DEFAULT_PROJECT_SETTINGS, PROJECT_SETTINGS_KEYS (auto via
Object.keys), the docs settings table, and parity + persistence tests.
Merger consumption lands in the follow-up engine change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The route was extracting getRunAuditEvents off scopedStore and calling it as
a bare function, which made this.db.prepare(...) throw. useMergeAdvanceNotice
silently swallowed the error, so the banner never rendered after merges.
Call the method on the store reference instead so this is preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds comprehensive test coverage for the run-audit system across the engine package, including lane session audit tests (triage, executor, reviewer, merger, heartbeat) and runtime audit invariants, plus a backcompat test for no-auditor scenarios.
Fusion-Task-Id: FN-5556
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5556
Implements a pull-based merge workflow by wiring the merger pull helpers from the engine, extending the git pull and stash routes, and aligning the `MergeAdvanceNotice` and `StashConflictModal` components to gate dismissal on stash drop. The `run-audit` module is updated with pull mutation documenta
Fusion-Task-Id: FN-5419
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5419
Adds a "session runtime resolved" audit event that flows through the engine's main execution lanes — triage, executor, reviewer, merger, heartbeat, step-session-executor, and mission-execution-loop — with runtime mutation support and test coverage, plus a compile-fix for the merger auditor wiring.
Fusion-Task-Id: FN-5544
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5544