Commit Graph

6674 Commits

Author SHA1 Message Date
gsxdsm
4c31e885bd feat(engine): merger auto-syncs project-root checkout after ref advance
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>
2026-05-23 14:41:11 -07:00
gsxdsm
db9928a4b1 feat(engine): export smartPull() library for stash-aware fast-forward
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>
2026-05-23 14:26:57 -07:00
gsxdsm
a201f56f09 feat(core): add mergeAdvanceAutoSync project setting
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>
2026-05-23 14:19:50 -07:00
gsxdsm
6083de214a fix(dashboard): preserve store this-binding in merge-advance-events endpoint
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>
2026-05-23 14:16:36 -07:00
gsxdsm
6ecaa717d6 feat(FN-5556): add run-audit agent session and runtime audit tests
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
2026-05-23 13:37:41 -07:00
gsxdsm
14bc63e813 feat(FN-5419): add stash conflict modal gating and smart pull routing for m
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
2026-05-23 13:06:48 -07:00
gsxdsm
7a20b95502 feat(FN-5544): emit runtime-resolved audit event across engine lanes
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
2026-05-23 12:14:07 -07:00
gsxdsm
8bee360906 feat(FN-5557): add join import in resolver and project command
Fixes FN-5557 by adding a missing `join` import to both `project-resolver.ts` and the project command handler, resolving an import error in the CLI package.

Fusion-Task-Id: FN-5557

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5557
2026-05-23 11:49:23 -07:00
gsxdsm
8f5c1f97ad feat(FN-5255): flip directMergeCommitStrategy default from squash to direct
Changes the default merge strategy from squash to direct by flipping `directMergeCommitStrategy` in the settings schema and types, with the core implementation in `merger-ref-update-advance.ts`. Also aligns a heartbeat executor test assertion with the FN-5060 deduplication shape.

Fusion-Task-Id: FN-5255

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5255
2026-05-23 11:08:08 -07:00
gsxdsm
ec1269fd84 feat(merger): auto-rehome FF-recoverable orphan commits in contamination recovery
Follow-up to bf4428c00 (FF-only ref advance). After the prevention fix
new orphans can't form, but pre-fix orphans like f6358ce4 on
fusion/fn-5419 still need a path back onto the integration branch.

Adds an `orphan-our-advance` classification to contamination recovery:
a "unique" foreign commit whose Fusion-Task-Id trailer points at a
`done` task AND that is unreachable from refs/heads/<integrationBranch>
is treated as a stranded merger output.

For these, the executor attempts a fast-forward rehome onto the
integration branch via advanceIntegrationBranchRef (which still enforces
the FF-only invariant). When successful, the orphan sha is added to the
existing `shasToDrop` set so the same recovery pass that drops
already-upstream/misrouted commits also drops the now-upstream orphan.

Non-FF orphans (diverged from current integration tip) are refused.
Doing a cherry-pick onto the integration branch from inside automated
recovery would introduce conflict-resolution surface that's too high
blast radius for a never-event recovery path. The refusal log line
includes the exact `git cherry-pick <sha>` command an operator can run
manually.

Two new GitMutationType audit events:
  - merger:orphan-rehome-ff (successful FF rehome)
  - merger:orphan-rehome-refused (non-FF, manual cherry-pick required)

Tests in merger-orphan-rehome.test.ts cover classification (orphan,
not-done, already-reachable, no-trailer) and the rehome operation
(FF success advances the ref + emits the audit event; non-FF refusal
emits the hint and leaves the ref untouched).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 09:59:50 -07:00
gsxdsm
bf4428c00c fix(merger): require fast-forward ref advances and read integration tip from refs/heads/<branch>
Closes a "non-fast-forward ref overwrite" path where a subsequent merger
could orphan a previously-merged squash by advancing the integration
branch to a sibling commit.

Symptom (observed on fusion/fn-5419): main reflog shows
  385b6e93 -> f6358ce4 (FN-5551 squash) -> 63ec7098 (FN-5552 squash)
with f6358ce4 and 63ec7098 both parented at 385b6e93. The FN-5551 squash
was correctly committed to main, then the FN-5552 merger built its own
squash off the stale 385b6e93 base and the CAS update-ref blindly moved
main sideways, orphaning f6358ce4 onto whichever feature branch had
already branched from it.

Two coupled fixes uphold the missing invariant — local <integrationBranch>
only advances via fast-forward, and the merger never builds a squash off
a stale base sha:

1. advanceIntegrationBranchRef: add a `merge-base --is-ancestor` check
   before update-ref. Non-FF attempts now return
   reason: "non-fast-forward-advance" instead of overwriting the ref.
   The existing concurrent-advance CAS guard is retained.

2. runMerge: resolve the integration-branch tip via
   `git rev-parse --verify refs/heads/<integrationBranch>` instead of
   `git rev-parse HEAD` in rootDir. In reuse-task-worktree mode rootDir's
   HEAD can lag behind the shared ref after a sibling merger advanced it
   via update-ref without re-checking-out.

Adds regression coverage in merger-ref-update-advance.test.ts: a
sibling-commit advance with a matching expectedCurrentSha is now refused
with the new reason, and multi-commit fast-forwards still succeed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 09:49:34 -07:00
gsxdsm
76429a820c fix(dashboard): pin mobile nav bar to page bottom when keyboard is open
Decouples the nav-bar keyboard-open class from the gated mobileKeyboardOpen
flag so the bar stays pinned to bottom: 0 regardless of modal state or
platform, instead of floating up with the iOS visualViewport.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 09:45:30 -07:00
gsxdsm
63ec709811 feat(FN-5552): narrow push outcome union in useMergeAdvanceNotice
Narrowed the push outcome union type in `useMergeAdvanceNotice` and added regression test coverage for the outcome narrowing behavior, including alignment of root script contract expectations in the package config tests.

Fusion-Task-Id: FN-5552

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5552
2026-05-23 09:00:02 -07:00
gsxdsm
385b6e93bb perf(engine): share a single git repo across merger-overlap-guard tests
The file previously did `mkdtemp` + `git init` + initial commit in each
test's beforeEach, paying ~5 git invocations per test. Move the repo
setup to beforeAll and add a `resetRepoToInitial` helper that uses
`git reset --hard` + branch cleanup + `git clean -fdx` between tests.
Safe because the file runs in the single-threaded engine-slow vitest
project.

Wall time: 17.1s → 10.3s (40% faster), 12 tests, all still passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 07:51:08 -07:00
gsxdsm
b1d185b93a perf(engine): tier 4 slow real-git test files into engine-slow project
`pnpm test` was dominated by a handful of merger and reliability-
interaction files that each spawn `mkdtemp` + `git init` + multiple
commits per test. Renaming them to `*.slow.test.ts` and routing them
to a new `engine-slow` vitest project moves them out of the default
local run.

Local `pnpm test` drops from 198s to 84s (~57% faster).

- `pnpm test` — engine-default + engine-reliability lanes only
- `pnpm test:slow` — engine-slow lane (4 files, 63 tests, ~37s)
- `pnpm test:all` — everything (for CI / verify:workspace)

Files moved:
- reliability-interactions/merge-reuse-task-worktree.test.ts (was 20.6s)
- merger-overlap-guard.test.ts (was 17.1s)
- merger-staging-allowlist.test.ts (was 11.8s)
- merger-diff-volume-gate.test.ts (was 8.4s)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 07:47:17 -07:00
gsxdsm
e5357a4afd feat(FN-5359): add push-to-origin button and hook to merge advance notice
Adds a push-to-origin workflow to the merge notice system, introducing a new `useMergeAdvanceNotice` hook, a `merge-advance-push-origin` route handler, and corresponding UI affordance in the `MergeAdvanceNotice` banner component. The engine gains TOCTOU and refusal audit assertions, and coverage exp

Fusion-Task-Id: FN-5359

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5359
2026-05-23 06:06:51 -07:00
gsxdsm
687237bd91 feat(FN-5411): add project identity recovery and identity-aware startup rea
Implements project identity tracking and recovery across the Fusion system (FN-5411), enabling persistent identity for projects across storage migrations, daemon reattaches, and CLI session management. Adds a project identity metadata API and central reattach ensure mechanism, wires identity stampin

Fusion-Task-Id: FN-5411

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5411
2026-05-23 04:18:55 -07:00
gsxdsm
fe58a57a7d feat(FN-5536): add retry-exhausted in-review policy convergence invariant
Adds a regression test for retry-exhausted in-review policy convergence behavior in the engine, exports `MAX_AUTO_MERGE_RETRIES` for test reuse, and includes a small fix to restore workspace build and test green in `merger.ts` and `self-healing.ts`.

Fusion-Task-Id: FN-5536

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5536
2026-05-23 04:05:25 -07:00
gsxdsm
4d1cad027c feat(FN-5444): add merge handoff test coverage for task worktree
Adds test coverage for merge queue and heartbeat handoff interactions (FN-5444), including source metadata expectations in heartbeat executor tests, merge handoff coverage gaps, and reuse scenarios in the merger worktree integration tests.

Fusion-Task-Id: FN-5444

Fusion-Task-Lineage: 45e1b43f-8ae3-46ba-a5ee-25e8c661e753

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5444
2026-05-23 03:50:05 -07:00
gsxdsm
d4ec82bf02 feat(FN-5528): add soft-delete exclusion to stale blocked-by recovery scrip
Adds `deletedAt` sweep guards to the engine's self-healing and merger to prevent recovery operations from processing soft-deleted tasks, filters deleted tasks in the `recover-stale-blocked-by` script, includes a new regression test for the deadlock-scan exclusion pattern, and updates the soft-delete

Fusion-Task-Id: FN-5528

Fusion-Task-Lineage: 5c9e45ca-49a8-47a0-a23d-6fe8e15e7e00

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5528
2026-05-23 03:33:53 -07:00
gsxdsm
2209c57dd6 chore(engine): remove workflow-step mock routing + stale FN-5482 docs
Drops the "workflow-step" MockSessionPurpose enum value and the
workflowStepId / workflowStepTemplateId plumbing through
agent-runtime, agent-session-helpers, mock-provider, executor, and
merger. The seeded-workflow-prompts script loses its FN-5205
rationale comment + test (no longer applicable now that workflow
steps run through the regular session purposes).

Also strips the stale FN-5482 architecture-invariant bullet from
AGENTS.md and the corresponding audit-event line from
docs/architecture.md (the self-healing reclaim invariant they
described no longer holds).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 03:09:00 -07:00
gsxdsm
ba6b41f533 chore(engine): remove triage broad-scope heuristics
Removes the BROAD_SCOPE_FLAG_VERSION/decideBroadScopeFlag pipeline,
the triage.ts call site that wrote `broadScopeFlag` source metadata
and emitted task:broad-scope-flagged-at-triage audit events, the
DatabaseMutationType enum entry, the diagnostic doc section, and the
associated unit + reliability-interaction tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 03:08:41 -07:00
gsxdsm
b1cf6fa3d1 chore(engine): silence unused-param warning in worktree-acquisition test
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 03:06:11 -07:00
gsxdsm
51fc826199 fix(engine,core): dedup heartbeat-spawned follow-ups by parent task
Same-agent intake guard now also matches siblings sharing a
sourceParentTaskId, so repeated heartbeats from one parent task
can't bypass dedup just because triage rewrites the title.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 03:05:46 -07:00
gsxdsm
02971efcfe fix(engine): skip promoted-foreign commits in contamination audit
assertCleanBranchAtBase now checks each foreign-attributed commit
against `git merge-base --is-ancestor <sha> main`. If the commit is
already on local main, it was promoted through integration regardless
of whose Fusion-Task-Id trailer it carries — treating it as foreign
contamination is wrong and was the root cause of the FN-5475 cascade
(downstream worktrees inherited a sibling task's tip during the brief
fast-forward window before main moved further).

Audit cost: O(N) extra git calls per audit run, where N is the number
of foreign-trailer commits in baseSha..branchName. Each call is ~5-10ms
and N is typically 1-5. Negligible relative to the surrounding I/O.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 02:59:23 -07:00
gsxdsm
a7ad30f22a fix(dashboard): keep mobile nav bar pinned when iOS keyboard opens
The visualViewport ICB compensation (intended for Android ICB-stuck-large
and pinch-zoom) was also reacting to iOS soft-keyboard shrinkage, pushing
the mobile bottom nav up above the keyboard. Pass the existing
`keyboardOpen` prop through to a `mobile-nav-bar--keyboard-open` modifier
class that pins `bottom: 0` so the keyboard simply covers the bar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 02:50:37 -07:00
gsxdsm
57f33ea2d2 test(engine): switch mockResolvedValueOnce → mockResolvedValue for resume-verifier overlap
The resume-path verifier in acquireTaskWorktree now also calls
classifyBootstrapMisbinding / reanchorBranchToBase before the
executor's primary contamination check runs. With a once-spy the mock
was depleted by the verifier, leaving the executor path with the real
implementation and the FN-4488 shape regression test no longer
exercised its expected recovery branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 02:41:08 -07:00
gsxdsm
e7088704e6 fix(engine): verify resumed worktrees aren't bootstrap-misbound
The resume path in acquireTaskWorktree returned a reused worktree
without checking whether its branch contained foreign commits. If a
sibling task's tip had been baked into the branch at creation time,
the executor preflight would later fail contamination checks forever
(observed in the FN-5475 cascade).

The resume path now computes a fresh merge-base and runs
classifyBootstrapMisbinding. For the foreign-only / zero-own-commits
shape it re-anchors inline and emits a branch:reanchor audit event.
Mixed contamination continues to flow through the executor's
primary recovery path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 02:32:01 -07:00
gsxdsm
9ce26eef26 fix(engine): un-deadcode bootstrap-misbinding auto-recovery fallback
The auto-recovery handler in branch-worktree.ts passed
foreignCommits: [] to classifyBootstrapMisbinding, and the classifier
gated isBootstrapMisbinding on foreignCommits.length > 0. The entire
reanchor block was dead code on this path — the FN-5475 cascade hit
"human adjudication" instead of recovering.

The classifier now derives the foreign-commit count from its own
git log walk; the input field is advisory/optional. Result type gains
foreignCommitCount. The fallback handler also stops using
ctx.task.baseCommitSha (deliberately stale per FN-4417) and computes
a fresh merge-base against local main / origin/main, matching the
executor's primary contamination path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 02:29:38 -07:00
gsxdsm
76bd3a7d90 feat(FN-5205): add workflow-step test mode dispatch and routing for mock pr
Implements workflow step test mode (FN-5205) by wiring mock dispatch, context forwarding, and routing through executor, merger, and mock provider, plus adding corresponding tests and docs. Also adds broad-scope triage heuristics to improve task-scope detection, touching triage.ts, triage-broad-scope

Fusion-Task-Id: FN-5205

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5205
2026-05-23 02:25:00 -07:00
gsxdsm
b22112af89 feat(FN-5482): suppress touch-synthesized mouse events on overlay dismiss
Adds a `useOverlayDismiss` hook that suppresses touch-synthesized mouse events on modal/dropdown overlays to prevent unintended close behavior on touch devices, with tests covering TaskCard dismissal and overlay interaction edge cases. Documentation updates in AGENTS.md and docs/architecture.md capt

Fusion-Task-Id: FN-5482

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5482
2026-05-23 02:06:11 -07:00
gsxdsm
f36abcc56e feat(FN-5521): derive secrets media parsing from hook query
Test coverage for mobile views: `MobileNavBar.test.tsx` now syncs its mock with the viewport constant, and `SecretsView.mobile.test.tsx` derives secrets media parsing from the hook query.

Fusion-Task-Id: FN-5521

Fusion-Task-Lineage: eb37d760-ab67-49c5-9b57-2c14d648a98a

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5521
2026-05-23 01:30:15 -07:00
gsxdsm
2498157ba5 feat(FN-5538): merge fusion/fn-5538 2026-05-23 00:42:46 -07:00
gsxdsm
2bee6b0824 fix(tests): drop SIGTERM handlers + skip flaky reconciliation test
The SIGTERM/SIGINT/SIGHUP handlers added to db.test.ts and the engine
tmp-dir tests re-raised signals after cleanup, which killed vitest itself
(exit 143) under the full engine reliability suite. Keep `afterAll` +
`beforeExit`/`exit` + lock-child kill — those cover the macOS file-handle
leak that was the actual driver of the merge-verification cascade.

Also skip project-engine-manager `retries failed project starts on
subsequent reconciliation ticks` — flake under full-suite load (30s
timeout) that passes in ~46ms standalone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:22:19 -07:00
gsxdsm
4e2e201215 feat(FN-5537): merge fusion/fn-5537 2026-05-23 00:13:27 -07:00
gsxdsm
1be0702155 fix(dashboard-mobile): clear Android status bar, footer-nav overlap, fetch toast spam
- Header.css: mobile padding-top is additive (var(--space-md) + env(safe-area-inset-top)) so the brand row keeps its 12px breathing room below the Android status bar instead of having it replaced by the inset.
- ExecutorStatusBar.css: bottom offset now uses max(env(safe-area-inset-bottom), 12px) to match the floor MobileNavBar already applies, so the footer lands flush on top of the nav instead of inside its padding band.
- useToast.ts: silently drop bare "Failed to fetch" error toasts (from fetch() aborts on tab background/resume); toasts with additional context still pass through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:03:22 -07:00
gsxdsm
d9fe33fec6 fix(FN-5456): tidy autocorrect shell quoting and disambiguate checkout
- Hoist `refs/heads/<name>` into a single quoted token before the verify
  call so the shell-quote boundary is unambiguous in the rendered
  command.
- Append `--` to the recovery `git checkout` so a same-named tracked
  path cannot win the DWIM resolution. The ref existence was already
  verified, so this can only resolve as the branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:03:12 -07:00
gsxdsm
d3ad0641c4 feat(FN-5486): merge fusion/fn-5486 2026-05-22 23:42:44 -07:00
gsxdsm
7345ab85d0 fix(engine-tests): plug fusion-test-ref-* leaks on SIGTERM fork recycle
Same class of failure as the kb-db-test-* leak: vitest's forks pool
SIGTERMs a fork on test timeout and skips the in-test `finally { rmSync }`,
leaking `fusion-test-ref-project-*`, `fusion-test-ref-concurrent-project-*`,
and `fusion-test-ref-advance-*` dirs that scripts/check-test-isolation.mjs
flags during deterministic merge verification.

Track every minted dir in a per-file set and sweep it from
SIGTERM/SIGINT/SIGHUP/beforeExit/exit handlers (signals re-raised after
cleanup) plus an `afterAll` for the happy path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:30:19 -07:00
gsxdsm
380f8b8e5e fix(core-tests): plug kb-db-test-* leaks on SIGTERM fork recycle
Vitest's forks pool SIGTERMs a fork when a test times out, which skips
`beforeExit`/`exit` handlers and leaves `kb-db-test-*` dirs behind.
`holdWriteLock` child processes also kept WAL/SHM handles open, blocking
recursive removal on macOS. Both paths now run cleanup: SIGTERM/SIGINT/
SIGHUP handlers sweep tracked dirs and re-raise the signal, and active
lock-helper children are tracked and SIGKILLed during cleanup so the
parent dir can be removed.

These leaks tripped scripts/check-test-isolation.mjs during deterministic
merge verification, failing auto-merge with "Completion handoff limbo
recovery exhausted" (e.g. FN-5521, FN-5486).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:27:44 -07:00
gsxdsm
2a3a07a612 feat(FN-5403): merge fusion/fn-5403 2026-05-22 23:12:49 -07:00
gsxdsm
3ccb132dcc feat(FN-5519): merge fusion/fn-5519 2026-05-22 22:43:26 -07:00
gsxdsm
394b9adc91 feat(FN-5518): merge fusion/fn-5518 2026-05-22 22:42:57 -07:00
gsxdsm
838002491d fix(executor): bypass summary-incomplete refusal for PREMISE STALE: summaries
Address the code-review finding on the prior commit: a natural premise-stale
summary like "PREMISE STALE: the task has no remaining work — implementation
is already done on HEAD" matches /\b(incomplete|not implemented|not done|
not finished)\b/i with 'the task' inside the 40-char first-person window,
refusing fn_task_done with summary-claims-incomplete and deadlocking the
escape hatch.

When summary starts (case-insensitive) with PREMISE STALE:, skip the
dissent-pattern and scoped-incomplete summary checks. Pending-code-review
and bulk-step-completion guards still apply unchanged.

Add executor-task-done-premise-stale.test.ts covering: the deadlock case
now passes; dissent phrasing in a sentinel summary is allowed; case-
insensitive sentinel; sentinel must be at the start (mid-summary doesn't
bypass); REVISE verdict still blocks even with the sentinel.
2026-05-22 22:34:32 -07:00
gsxdsm
8a3afcf9d4 fix(executor+engine-tests): preflight premise-stale exit and serialize reliability suite
Root-cause prevention for the FN-5521 failure class where PROMPT.md describes
work that is already done on HEAD and the executor still marches through
plan/review/test/doc, then merger burns hours retrying against unrelated
flakes in @fusion/engine reliability-interactions.

- Executor system prompt: add a Preflight escape hatch. When Step 0
  reproduces and finds HEAD already matches the desired state, the agent
  marks Step 0 done, marks remaining steps skipped, and calls fn_task_done
  with a `PREMISE STALE:` summary. Reuses the existing
  evaluateTaskDoneRefusal/skipped semantics and the merger's
  empty-own-diff fast-path — no new tools or refusal classes needed.

- packages/engine/vitest.config.ts: split into two projects. engine-default
  retains full parallelism; engine-reliability scopes
  src/__tests__/reliability-interactions/** to
  poolOptions.threads.singleThread so event-ordering assertions stop
  flaking under workspace-concurrent merge-gate load (the
  `expected 24 to be less than 19` rowid interleaving in
  merge-reuse-task-worktree).
2026-05-22 22:26:18 -07:00
gsxdsm
23a0c27bd5 fix(engine-tests): subprocess timeout attribution + ENOTEMPTY retry on cleanup
Two test-infrastructure fixes from agent #1's flake-stabilization pass.
Skipped its DESC-order assertion rewrite (semantically equivalent no-op
that conflicted with the prior boilerplate consolidation in 848a226ca)
and its vitest.config additions (already in flight on main).

1. vitest-setup.ts: completedSubprocessFailures was a plain string[]. When
   a 30s subprocess-guard timer fired during a *later* test's execution
   window (because the owning test ran for e.g. 40s under its 60s
   timeout budget), the failure surfaced in the innocent successor test's
   afterEach. Typed the array as { ownerTestName, message }[] and filter
   on the current test name; orphaned entries are dropped silently.

2. worktree-contamination-attribution.real-git.test.ts: afterEach rm
   occasionally hits ENOTEMPTY on macOS when a git rebase internal dir
   isn't fully flushed. Added maxRetries: 3, retryDelay: 100.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 22:10:45 -07:00
gsxdsm
ed4575cc6f fix(merger): quote pnpm filter args + git refs, widen out-of-scope detection
Code-review fixes for 036387610 / d02cd38d7:

1. `deriveScopedPnpmTestCommand` now passes each `--filter` argument through
   `quoteArg`. Package names come from workspace `package.json` files which
   are not strictly trusted input — a metacharacter in a name would have
   leaked into the shell command.

2. `getBranchChangedFiles` now quotes both git refs in the `<base>...<head>`
   range. Branch names can legally contain `/` and other characters; this
   is defense-in-depth consistent with the rest of merger.ts.

3. Out-of-scope detection is now package-aware via a new
   `packageNamesForFiles` helper. A failure in `__tests__/foo.test.ts` is
   correctly treated as in-scope when the branch touched `src/foo.ts` in
   the same package, whereas the previous filename-prefix heuristic missed
   that case entirely. The dead `bf.startsWith(ff/)` clause is removed.
   Falls back to the directory-prefix heuristic when pnpm-workspace.yaml
   is unavailable.

195 tests pass across merger-verification.test.ts and the reused
merge-reuse-task-worktree.test.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 22:06:18 -07:00
gsxdsm
848a226cab refactor(engine-tests): consolidate merge-reuse-task-worktree boilerplate
Each of the 18 tests in this suite was ~50 lines of identical setup: rename
main→master, mark steps done, create the branch with one content commit,
create the task worktree, set task.worktree, enqueue the merge queue. The
test bodies were dominated by ceremony and the unique assertions were buried.

Consolidated into a single `setupReuseHandoff` helper that takes per-test
overrides (skipWorktreeAdd, worktreeOverride, skipEnqueue, emptyOwnDiff,
extraSettings). Added a top-level beforeEach to clear shared mock/registry
state that several tests forgot to reset.

No behavioral change. All 21 tests pass on two consecutive runs in ~20s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:44:23 -07:00
gsxdsm
bdfe620fab fix(FN-5456): tighten branch-autocorrect verify and add real-git regression
Follow-up to a3ec2e55c addressing code-review feedback:

- Restrict the existence check to local heads via
  `git show-ref --verify --quiet refs/heads/<name>` so a stray tag or
  remote ref with the same name cannot satisfy the check and lead the
  subsequent `git checkout` to a detached HEAD on the wrong object.
- Use `git branch -M` (force) instead of `-m` for the rename path so
  case-only renames succeed on case-insensitive filesystems (macOS,
  default Windows).
- Document the FN-5456 invariant on the function's doc comment.
- Add a real-git regression suite covering the three paths the fix
  governs: no creation when expected ref is missing, switch-only when
  it exists, and rejection of same-named tags.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:40:40 -07:00
gsxdsm
a3ec2e55c8 fix(FN-5456): never create task branches from arbitrary HEAD in autocorrect
`attemptBranchAutocorrect` was the only branch-creation site in the
engine that ran `git checkout -B <expected>` without a start point.
When the worktree's HEAD was at a previous occupant's tip, the new
label silently captured that commit — the "branch: Created from HEAD"
contamination pattern that the cross-contamination guard then refuses
to auto-resolve (see FN-5456: orphan FN-5477 commit 268574b9a stranded
on fusion/fn-5456).

Replace the unsafe fallback with verify-then-`git checkout`: only switch
to an existing expected ref; return `failed` when it does not exist so
upstream recovery — which knows the proper base SHA — can re-anchor via
`prepareForTask` / `reanchorBranchToBase`.

Tests updated for the new command sequence; new case covers the
"expected ref missing → fail without creating from HEAD" guarantee.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:36:02 -07:00