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>
Live fusion instances running on a shared HOME write to tasks/, messages/,
memory-insights.md, test-cache.json, HEARTBEAT.md, kb.db.backup-*, and
fusion.db.pre-* snapshots; none were in the runtime ignore list, so the
script flagged them as test-driven mutations and failed merge build
verification. Recursion in collectFusionSignature only filters via top-level
matches, so adding these top-level patterns short-circuits descent into
live-app dirs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Expand test-changed coverage and shard selection assertions for CI workflows
- Improve vitest worker temp-directory utilities and related core/CLI tests
- Refine test isolation guardrails and runtime ignore handling for live .fusion noise
- Update contributing guidance and root test script usage for the verified workflow
Fusion-Task-Id: FN-3607
This merge introduces eval score categorization with a new `eval-scoring.ts` module (FN-3601, FN-3390), hardened bundled plugin entry resolution to prioritize workspace source over installed copies (FN-3590), and documented reply-link threading behavior in the mailbox (FN-3598). It also adds mobile
Fusion-Task-Id: FN-3590
Local `pnpm test:isolated` was failing because a concurrently-running
fusion app continually mutates `~/.fusion` (databases, agent sessions,
memory, automations, plugins, logs). Filter those runtime-owned paths
from the protected-dir signature, widen the baseline-stability sampling
window, and re-sample on suspected violations so transient app activity
doesn't masquerade as test pollution.
Tests still cannot legitimately write into these paths — they're skipped
because the *running app* is expected to.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the live reviewer override behavior in the settings reference and task management guides, adding two lines to each file for a total of 4 lines of documentation.
Fusion-Task-Id: FN-3595
This merge lands five FN-3593 commits establishing a test isolation contract with a new `scripts/check-test-isolation.mjs` guard that scans for accidental `beforeEach`/`afterEach`/`beforeAll`/`afterAll` in setup helpers, plus per-package `setup-test-isolation.ts` bootstraps that canonicalize the pat
Fusion-Task-Id: FN-3593
Adds test coverage for incomplete runtime dist trees in the test-artifacts script, with one new test file and a small fix to the script itself.
Fusion-Task-Id: FN-3297
Merges FN-3470 in two steps: first, runtime plugins now prefer source path aliases over build artifacts, with hardening tests for vitest alias resolution and plugin view registry updates; second, a new `ensure-test-artifacts.mjs` bootstrap script guarantees required test files exist before test runs
Fusion-Task-Id: FN-3470
- Add missing top-level dashboard views section to the PLUGIN_AUTHORING table of contents
- Renumber downstream section headings and TOC entries to keep numbering sequential
- Add a node:test suite that validates heading order and TOC anchor alignment
Fusion-Task-Id: FN-3468
Merges FN-3311 work into the release pipeline: adds a version notes extractor with tests and integrates GitHub release creation into the release script, plus a minor CSS design token consistency fix in the dashboard.
Fusion-Task-Id: FN-3311
This merge adds a new llama.cpp local AI provider integration, including a `pi-llama-cpp` CLI extension package with model resolution and context retrieval, a `LlamaCppProviderCard` UI component for provider configuration, backend API routes for model registration and llama.cpp probe/health checking
Fusion-Task-Id: FN-3377
Adds `list_agents` and `delegate_task` tools to the pi extension for inter-agent delegation, along with supporting reference docs and 248 new tests. The sync script was also updated to keep the skill tools in sync with the extension implementation.
Fusion-Task-Id: FN-3327
This merge refactors the test-changed script and adds new CLI tests (FN-3234), keeping automation on the full-suite path. It includes a new test file for the root test command, updates to the CI workflow, and documentation improvements.
Fusion-Task-Id: FN-3234
Split packages/dashboard/app/__tests__/api.test.ts (~6.4k lines) into per-area files (auth, git, missions, projects, settings, tasks), and src/__tests__/routes.test.ts (~20k lines) into per-area files (agents, auth, automation, git, github, planning, settings, system, tasks, tasks-ops). The monolith files were dominating wall-clock for the dashboard suite under file-parallel execution.
Also document in packages/core/vitest.config.ts why the core suite cannot move to "threads": vitest-setup gates per-worker cwd on isMainThread (false in worker_threads, so isolation breaks), and setup-test-isolation writes process.env.HOME unconditionally (threads share env, so concurrent workers race).
Drop an unused execFileSync import from scripts/test-with-lock.mjs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- scripts/test-with-lock.mjs: pnpm test:locked acquires an exclusive
~/.fusion/test.lock (O_EXLOCK) before running pnpm test, so several
Claude Code worktrees on the same machine serialize their vitest
fan-out instead of saturating the box. Prints the holding PID and
worktree path while waiting.
- dev-server-manager test children now park on stdin instead of
setInterval so manager.shutdown() can deterministically exit them
via stdin close, preventing orphaned node processes outliving the
test run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes the insight tool filter typing in the CLI extension to use stricter types, preventing incorrect filter values from being passed through.
Fusion-Task-Id: FN-3035
The single "smart" strategy is now two flavors with the new default flipped
to prefer-main. Both share a pre-cascade `git fetch origin <currentBranch>`
+ best-effort fast-forward so a freshly-pushed sibling commit doesn't get
clobbered when the fallback resolves a conflict against a stale base.
- "smart-prefer-main" (new default): -X ours fallback. Protects just-merged
sibling work from being regressed by a concurrent task branch.
- "smart-prefer-branch": -X theirs fallback. Equivalent to legacy "smart".
Legacy "smart" / "prefer-main" enum values are accepted and normalized via
`normalizeMergeConflictStrategy()` so existing settings.json files migrate
seamlessly. The fast-forward step gracefully degrades on fetch failure or
divergent local main (logs and continues).
Updates settings UI dropdown, test helpers, and adds 5 fetch+ff regression
tests + 7 normalize-helper tests. Lint cleanup of two empty catch blocks
in scripts/release.mjs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dry-run gate was at the bottom of the script, after version bump,
lockfile update, CHANGELOG sync, build, and the chore(release) commit
had already run — so a dry-run left the local repo with a stray version
commit that had to be reset. Move the gate to right after the version
selection so dry-run shows the full preview (changesets, proposed
version, override prompt) and exits before mutating anything. Drop the
now-dead late check and the redundant DRY_RUN short-circuit in confirm().
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dry-run was previously bundled with --yes for the version prompt, so
it silently accepted the proposed version and skipped the interactive
flow it's meant to exercise. Only --yes should auto-accept.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Show pending changeset summaries, compute the proposed semver via
`pnpm changeset status --output`, and prompt for an override before
applying. If the user picks a different version, post-process every
fixed-group package's package.json + CHANGELOG.md heading so the
commit, npm publish, and tag all use the chosen version. --yes and
--dry-run skip the prompt and accept the proposed version.
Adds a changeset for the recent SetupWizard scroll + model onboarding
handoff fixes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add .cmd wrappers for audit-squash-merge, check-test-isolation, dev-hmr, dev-with-memory, release, and sync-fusion-skill-tools scripts
- Resolve each wrapper relative to its own script directory via %~dp0 so calls work from any working directory
- Forward all CLI arguments with %* to preserve existing script behavior under cmd.exe
- Add generated engine-tools reference and refresh extension/capabilities docs from source definitions
- Enhance sync-fusion-skill-tools script to verify and maintain skill documentation consistency
- Expand skill-sync tests and enforce sync:fusion-skill:check in the workspace test pipeline
- Add FN-2569 changeset documenting the published @runfusion/fusion patch update
The MCP config was generated lazily once and locked, so engine session-scoped
tools (fn_review_spec, fn_review_step) never reached the Claude CLI subprocess
and triage/executor sessions failed with "unknown tool" errors. Now the config
is hashed per call and rewritten when the tool set changes.
Also adds scripts/sync-fusion-skill-tools.mjs to regenerate the SKILL.md
tool-categories block from extension.ts at build time, with a --check mode
wired into skill-sync tests so drift fails CI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
That flag was added for diagnosing the Ink/perf_hooks leak. Now that the
leak is fixed, leaving it on means a 1GB+ snapshot file gets dumped to
cwd whenever heap pressure hits — noisy and not useful day-to-day.
Inspector flag forwarding is kept for normal --inspect usage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TUI: vitest memory-guard threshold and on/off toggle now persist to
global settings (vitestAutoKillEnabled / vitestKillThresholdPct), so
they survive dashboard restarts. Stats panel shows the system-memory
used percentage next to used/free. Utilities panel exposes [+/-] to
adjust the threshold in 5% steps (50–99%).
Release: scripts/release.mjs auto-syncs a root CHANGELOG.md aggregated
from every packages/*/CHANGELOG.md, grouped by version with one
sub-block per package.
Versioning: all private @fusion/* packages joined the changesets fixed
group with the public cli + cli-alias and were aligned to 0.2.5, so
every release bumps every package and produces per-package CHANGELOG
entries that the aggregator picks up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NODE_OPTIONS is inherited by every grandchild node process. With --inspect
in there, every vitest worker, agent subprocess, and claude invocation the
dashboard spawned tried to bind 127.0.0.1:9229 and emitted "address already
in use" — those failure strings ended up in agent logs (visible in heap
diagnostics). Pass inspector flags directly on the node command line so
they apply only to the dashboard process.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ink/react-reconciler in dev mode emits performance.mark/measure() on every
render. Without a PerformanceObserver, Node retains every entry in the User
Timing buffer forever — ~165 renders/sec in the TUI accumulate ~600MB of
PerformanceMeasure objects + diff strings ("Components ⚛", "Changed Props",
"– children", etc.) over 20-30min, which is what was OOM-crashing long dev
sessions. A 30s interval calling clearMeasures()/clearMarks() drains it.
Also teach scripts/dev-with-memory.mjs to forward --inspect /
--inspect-brk, plus auto-add --heapsnapshot-near-heap-limit=3 so any future
heap-limit OOM auto-dumps. The script now invokes node directly with tsx's
preflight + loader (resolved via require.resolve) instead of going through
the .bin/tsx wrapper, so the inspector attaches to the real app process
instead of a parent wrapper that exits before serving the websocket.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add refresh-page actions alongside retry/reinitialize in TerminalModal bootstrap and xterm init error states
- Add terminal error action-group styling so multiple recovery buttons render cleanly
- Extend TerminalModal tests to cover new refresh controls and verify window reload behavior
- Remove unused TaskLogEntry import and drop the stale hooks eslint suppression in dashboard-tui app
The FN-2370 auto-resolved squash (7c1a1c36c) reverted three commits' worth
of refinements to the claude-cli provider because the branch contained
rebased duplicates of commits already on main. The auto-resolver picked
the older side line-by-line and dropped the newer.
Restored:
- /api/models filter logic (was inverted; emptied every model picker)
- Claude Opus 4.7 catalog entry in pi-claude-cli
- Provider card status text and toast messages (no longer claim a restart
is needed — the extension is always-loaded now)
- POST /api/auth/claude-cli returns restartRequired: false
Prevention:
- Regression tests on the /api/models useClaudeCli filter
- scripts/audit-squash-merge.mjs flags duplicate-cherry-pick risk and
touched-file overlap on any squash commit
- AGENTS.md documents the rebase-before-squash rule and requires the
merging agent to run the audit and triage every flagged item itself
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- POST /api/agents/:id/runs now performs preflight validation of effective task
- Rejects closed tasks (done/archived) with 409 before calling executeHeartbeat
- 409 response includes clear error string naming task ID + column and structured details
- Existing active-run 409 behavior remains unchanged and still takes precedence
- Added tests for stale-task preflight validation
Wraps the changesets flow into one command: preflight (branch/clean/up-to-date),
apply changesets (version bump + CHANGELOG), build, commit, publish to npm, push
tag. The tag push triggers release.yml for platform binary builds.
Trade-off vs the version.yml CI workflow: no npm provenance (OIDC is CI-only).
Use the CI workflow when provenance matters.
Flags: --dry-run (no publish/push), --yes (skip confirmation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the engine restarts mid-step, an in-progress step may have already
passed plan + code review but not yet been flipped to done by the agent's
next task_update call. Previously, the next executor pass re-entered the
step and replayed both reviews — measured at 5-20 min of pure waste per
restart (observed in FN-2215 Step 1 and FN-2207 Step 6).
recoverApprovedStepsOnResume scans the task log for any in-progress step
whose most recent "code review Step N: APPROVE" entry is newer than its
most recent "Step N → pending" transition, and marks those steps done
before execute() runs. Safely skips steps that were reset after approval
(e.g. by a workflow revision) or only received REVISE verdicts.
Called from both the engine-restart path (resumeOrphaned) and the
unpause path, matching the two places the task log shows as vulnerable
to this race.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce a shared test-utils module and global vitest setup that
guarantee tests never write to the real .fusion directory or leak temp
directories under /tmp.
Infrastructure:
- packages/core/src/__test-utils__/workspace.ts — tempWorkspace(),
useIsolatedCwd(), trackForCleanup(), assertOutsideRealFusion() with
auto-cleanup in afterEach.
- packages/core/src/__test-utils__/vitest-setup.ts — per-worker guard:
chdirs each worker into an isolated tmp dir, wraps process.chdir to
refuse the real .fusion, scopes tmp dirs under fusion-test-workers/
(skips cwd change in thread-pool workers where chdir isn't supported).
- packages/core/src/__test-utils__/vitest-teardown.ts — globalSetup
hook that wipes the shared parent even when workers are SIGKILLed.
- scripts/check-test-isolation.mjs + `test:isolated` / `test:check-
isolation` scripts for CI.
- @fusion/test-utils alias + setupFiles + globalSetup wired into core,
cli, engine, dashboard, tui vitest configs; matching tsconfig paths.
Test refactors (no behavior change):
- cli provider-settings, auth-paths, provider-auth — switch leaking
mkdtempSync calls to tempWorkspace().
- core migration, first-run, store-backward-compat — replace manual
process.chdir save/restore with useIsolatedCwd().
- tui fusion-context — replace 9 hardcoded tmp paths (collision-prone
under parallelism) with tempWorkspace().
- dashboard useTheme, FileBrowser, TaskCard — resolve source-file reads
against a PACKAGE_ROOT computed from import.meta.url instead of cwd,
so tests don't depend on the process working directory.
Verified: full suite (~15,500 tests across 8 packages + plugins) passes
and the orphan-detector reports zero leaked temp directories after a
complete run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove the anti-pattern where the cwd project was treated as "primary" with a
special engine, and other projects got "secondary" engines through a separate
code path. Every project now gets an identical ProjectEngine created through
ProjectEngineManager.
Key changes:
- Add ProjectEngineManager class to @fusion/engine for uniform engine lifecycle
- Replace manual engine maps in dashboard.ts and serve.ts with engineManager
- Add engineManager to ServerOptions for per-project engine resolution
- Add getProjectContext() helper in routes.ts (replaces 199 getScopedStore calls)
- Merge and automation routes now resolve engine subsystems per-request
- SSE endpoint uses engine's store when available (same EventEmitter)
- Fix tsx not found in dev-with-memory.mjs startup script
- Add invalidateAllGlobalSettingsCaches for cross-project settings sync
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add scripts/dev-with-memory.mjs entrypoint script
- Configure memory monitoring and optimization for development mode
- Update package.json scripts to include memory-aware dev option
- Add macOS signing script with codesign, notarization, and stapling support
- Add Windows signing script using signtool with PFX certificate
- Integrate signing steps into release and test-release workflows
- Add signing workflow tests and verification coverage
- Add CODE_SIGNING.md documentation and update README