Adds a per-machine singleton lock that engages in
ProjectEngineManager.createAndStart() before any engine subsystems
spin up. Two fn dashboard processes can no longer run engines for the
same project on the same host — previously they would share .fusion/
state and corrupt worktrees / task rows for in-process projects.
The guard combines two independent checks:
- A proper-lockfile file at <project>/.fusion/engine.lock with
stale-lock recovery (auto-released on process death).
- A loopback listener on a hashed per-project address — UDS on
POSIX, named pipe on Windows. Stale UDS files are probed and
unlinked before a retry bind.
Failures raise EngineAlreadyRunningError. Both guards are released
from stopAll() and pauseProject(); a release on engine.start()
failure lets retries re-acquire cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add canonical working-directory resolution with node mapping support in core and engine paths
- Update runtime/multi-project documentation and include published-package changesets for the merged work
- Migrate roadmap dashboard surface to the bundled plugin registry flow and adjust lazy-view integration/tests
- Default non-ephemeral agents to active state and extend related agent/route/executor test coverage
Fusion-Task-Id: FN-3508
- Add pauseProject() and resumeProject() methods to ProjectEngineManager
- Wire pause and resume routes to engineManager with proper error handling
- Update frontend useProjectActions hook to use dedicated pause/resume APIs
- Add comprehensive tests for pause/resume in ProjectEngineManager
- Add route tests for project pause/resume with engineManager mocks
- Fix mock stubs for getProject/updateProject/updateProjectHealth
- ProjectEngineManager.startReconciliation() polls for newly registered
projects every 30s and starts their engines without requiring UI access
- Expose global concurrency limit in dashboard settings
- Fix SettingsModal test cleanup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ProjectEngineManager now creates a single AgentSemaphore and injects it
into all engines via config.globalSemaphore. Previously each engine created
its own semaphore, so the globalMaxConcurrent limit was not enforced across
projects. The semaphore dynamically reads the limit and listens for
concurrency:changed events for live updates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docs(FN-1626): document PWA home bar gap token and updated bottom-layout contract
- fix(pre-existing): add missing invalidateAllGlobalSettingsCaches export to unblock build
- feat(FN-1626): update regression tests for standalone spacing and PWA home bar gap
- feat(FN-1626): add PWA home bar gap token and update bottom-layout CSS contract