Commit Graph

7875 Commits

Author SHA1 Message Date
gsxdsm
2f80c6ea7b chore(release): v0.34.0
Version bump via changesets.
2026-05-26 23:45:59 -07:00
gsxdsm
390bd7f923 perf(dashboard): cache gh CLI checks and defer SQLite integrity scan
Cold-start dashboard responsiveness went from ~99s to ~6-11s. CPU profiling
identified two synchronous-spawn hotspots blocking the event loop:

- `GitHubTrackingReconciler` scanned up to 200 done tasks per startup,
  each call into `getIssue` invoking `isGhAvailable()` + `isGhAuthenticated()`
  via `execFileSync`. `gh auth status` makes a network roundtrip, so 400
  sync spawns ≈ 71s of pure event-loop blocking (69% of cold-start CPU).
  Memoized both checks with a 60s TTL; `resetGhAvailabilityCache()` is
  exported for login/logout flows that need immediate invalidation.

- `PRAGMA integrity_check(100)` walks every page of the SQLite file (~7s
  per database, multiple DBs × projects). The deferred check was scheduled
  3s after init — right in the responsiveness-critical window. Pushed to
  60s so the user is already interacting before it runs; check itself is
  unchanged.

Also yields the event loop between major InProcessRuntime init phases and
between self-healing recovery steps (34 per project), defers orphan-task
AI agent resumption by 30s (env-overridable, auto-zero under Vitest), and
ships an opt-in `FUSION_TRACE_EL_LAG=/path/to/file` event-loop lag tracer
that diagnosed all of the above.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 23:41:28 -07:00
gsxdsm
cac08d733b chore(dashboard): fix lint and typecheck
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 22:20:22 -07:00
gsxdsm
4e4830f592 fix(engine): harden merge finalize SQLite bind and recover bare merge subjects
Two compounding bugs surfaced as `feat(FN-XXXX): merge fusion/fn-XXXX`
commits landing on main:

1. The verification-fix finalize path could bind `undefined` to SQLite
   parameter 4 (`commitSha`) of `upsertTaskCommitAssociation` under the
   parallel-attempt race, failing the merge over a denormalization
   write after the commit had already landed. Centralized both
   duplicated callsites into a helper that validates each git output
   before binding.

2. Four self-healing/aiMergeTask recovery sites copied
   `classification.commit.subject` verbatim into
   `mergeDetails.mergeCommitMessage`, persisting the tier-3
   `merge ${branch}` fallback when it ended up on the landed commit.
   New `regenerateBareMergeSubject` helper detects the bare pattern
   and rebuilds a descriptive subject via the AI summarizer. Cosmetic
   only — the git commit is not amended.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:57:31 -07:00
gsxdsm
6a6c6fdbfd perf(dashboard): speed up startup and eliminate API request storms
Multiple coordinated fixes for the perceived "dashboard takes forever to
load" complaint. Per-page-load HTTP requests drop from ~177 to ~101 and
duplicate per-project InProcessRuntime creation is eliminated.

- engine: shouldUseHybridExecutor no longer auto-enables for local-only
  multi-project setups (set FUSION_HYBRID_EXECUTOR=1 to force). The
  duplicate-runtime path was running self-healing twice per project and
  contending on the same SQLite file. ProjectEngineManager already
  handles N local projects with one InProcessRuntime each.
- dashboard cli: parallelized independent store inits, started
  CentralCore.init early in background, ran plugin loading concurrently
  with extension resolution. Sequenced SQLite store inits to avoid a
  TOCTOU race in addColumnIfMissing migrations across TaskStore /
  AutomationStore / PluginStore / AgentStore (all open the same
  .fusion/fusion.db). Restored try/catch around HybridExecutor.initialize
  and engineManager.ensureEngine so a paused or broken cwd project no
  longer aborts dashboard startup.
- dashboard client: added in-flight request dedupe wrapped around the
  top API offenders. /api/plugins/ui-slots drops from 17x to 1x per load.
  dedupe.forceFresh redirects ALL in-flight waiters to receive the fresh
  post-mutation response, not just the forcing caller. Generation
  counters in useAgents and AgentListModal protect against slow polls
  overwriting fresh state.
- dashboard SSE: agent event handler now debounces 250ms with a
  trailing-edge guard so multi-agent activity bursts coalesce to at
  most 2 refetches per burst.
- dashboard route: PATCH /api/projects/:id with isolationMode change
  returns 503 with actionable guidance when HybridExecutor is
  unavailable, instead of silently persisting a config the live runtime
  won't honor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:57:31 -07:00
gsxdsm
9bea9510c0 Merge pull request #1073 from titosemi/fix/chat-manager-multi-project-routing
fix(chat): use per-project ChatManager for multi-project message routing
2026-05-26 21:57:24 -07:00
gsxdsm
0cabe582aa fix(chat): resolve ChatManager before flushing SSE headers
Move resolveScopedChatManager() before res.flushHeaders() in the
POST /messages route so that failures (e.g. project DB cannot be
opened) produce a proper HTTP error instead of silently closing the
SSE connection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:44:12 -07:00
gsxdsm
e721d8481a Merge origin/main into fix/chat-manager-multi-project-routing
Resolve conflict in register-chat-routes.ts: keep per-project
ChatManager routing via resolveScopedChatManager while preserving
main's resolveProjectChatContext-based store resolution for read
routes. The auto-merged chat-project-services.ts correctly combines
main's fallback-safe resolveProjectChatContext with the PR's new
getOrCreateScopedChatManager cache.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:38:25 -07:00
gsxdsm
903806f6fb Merge pull request #978 from titosemi/fix/chat-view-message-truncation
fix(dashboard): wire scroll-to-top pagination for chat history instead of loading all messages
2026-05-26 21:20:23 -07:00
gsxdsm
d99cfaf51a feat(FN-5594): add Windows ARM64 build support and test assertions
Added Windows ARM64 support to the desktop build pipeline, introducing separate target architecture arrays for x64 and ARM64, configuring electron-builder to produce artifacts for both platforms, and adding tests to assert the correct architecture names.

Fusion-Task-Id: FN-5594

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5594
2026-05-26 17:19:35 -07:00
gsxdsm
202895c319 feat(FN-5593): add windows desktop build jobs to release workflows
Adds a Windows desktop build job to the release pipeline, wires the desktop artifacts into both release and test-release workflows, includes workflow shape assertions in tests, and documents the Windows release artifacts in the desktop README.

Fusion-Task-Id: FN-5593

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5593
2026-05-26 17:19:35 -07:00
gsxdsm
a4628c8649 feat(FN-5592): merge fusion/fn-5592 2026-05-26 17:19:35 -07:00
gsxdsm
40f7cad3e0 feat(FN-5587): add windows desktop packaging workflow, scripts, and tests
Adds Windows desktop packaging support for Fusion, including a new GitHub Actions workflow for building Windows desktop targets, matching build scripts in the root and desktop package, a test for electron-builder configuration, and documentation of the packaging path.

Fusion-Task-Id: FN-5587

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5587
2026-05-26 17:19:34 -07:00
gsxdsm
aa64643cf1 feat(FN-5589): merge fusion/fn-5589 2026-05-26 17:19:34 -07:00
gsxdsm
4b5a2e5c68 feat(FN-5588): add secrets section navigation to Settings modal
Adds a dedicated Secrets section to the SettingsModal navigation, removes the now-unused secrets footer callback wiring from App and AppModals, updates the related tests, and documents the secrets location in the dashboard guide.

Fusion-Task-Id: FN-5588

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5588
2026-05-26 17:19:34 -07:00
Josemi Liebana
e018701fed fix(dashboard): wire scroll-to-top pagination instead of loading all messages
- Remove fetchAllMessagesInChat/fetchAllMessages helpers
- Keep limit:50 for initial load in useChat and useQuickChat
- Add IntersectionObserver sentinel at top of ChatView message list to
  trigger loadMoreMessages() when user scrolls to the top
- Keep stale-session guards (activeSessionRef checks) from original PR
- Tests: revert assertions back to { limit: 50 }
2026-05-26 21:48:35 +02:00
Josemi Liebana
30e14650cd fix(chat): scope chatStore in stream+messages session guards
The session existence checks in GET /stream and POST /messages were using
options?.chatStore (global DB), so secondary-project sessions would throw
notFound before the scoped ChatManager ran. Replaces with resolveScopedChatStore.
2026-05-26 18:11:44 +02:00
gsxdsm
91b5d77934 Merge pull request #974 from titosemi/fix/chat-sessions-multi-project-scope-clean
fix(dashboard): use per-project chatStore in chat session API routes
2026-05-26 08:27:02 -07:00
Josemi Liebana
d6d5bf098d fix(lint): prefer-const in context.ts; fix GET /sessions/:id isGenerating routing
- context.ts: let engine → const engine (prefer-const lint rule)
- register-chat-routes.ts GET /sessions/🆔 use per-project chatManager
  for isGenerating enrichment (addresses Greptile P2 comment)
2026-05-26 16:58:26 +02:00
Josemi Liebana
0ce1b62809 fix(chat): use per-project ChatManager for multi-project message routing
In multi-project mode the global chatManager was backed by ~/.fusion/fusion.db.
Secondary project chat sessions live in per-project DBs, so
chatManager.sendMessage() failed with 'Chat session not found' for any
project that is not the daemon's CWD.

Fix: add getOrCreateScopedChatManager to chat-project-services.ts (cached by
fusionDir, same pattern as the existing chatStore cache). POST /messages,
GET /stream, POST /cancel, and GET /sessions isGenerating enrichment all
resolve the per-project ChatManager when projectId is provided.

Fallback to global chatManager when no projectId (preserves single-project mode).

Tests: added multi-project chat routing describe block in chat-routes.test.ts.
2026-05-26 16:16:38 +02:00
Josemi Liebana
dae167a127 fix(test): fix 7 test failures in chat-routes and routes-agents suites
Group A (2 failures): resolveProjectChatContext was calling
getOrCreateProjectStore even when no engine existed for the
project. This triggered real FS lookups in tests, causing the
catch to return the default store instead of the engine store.
Fix: only take the engine path when an engine is actually found.

Group B (1 failure): createSSERequest() returned a bare
EventEmitter with no .query property. The refactored handler
reads req.query.projectId at the top, throwing TypeError.
Fix: initialise .query = {} in createSSERequest().

Group C (4 failures): createMockStore() in routes-agents.test.ts
was missing getFusionDir(), which getOrCreateScopedChatStore()
calls to compute the cache key. The resulting TypeError caused
every lookup=resume handler invocation to 500.
Fix: add getFusionDir() to the mock.
2026-05-26 15:58:57 +02:00
gsxdsm
1fb905afb7 feat(FN-5585): add planning branch controls to planning mode modal
Adds planning branch controls to the PlanningModeModal, extending the planning API and routes with supporting tests and documentation. The changeset bumps `@runfusion/fusion` as a minor release.

Fusion-Task-Id: FN-5585

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5585
2026-05-25 16:32:57 -07:00
gsxdsm
5eacd79b43 feat(FN-5584): merge fusion/fn-5584 2026-05-25 15:27:06 -07:00
gsxdsm
06a107dcba fix(FN-5584): restore planning fallback when primary provider API key is missing
The top-level promptWithFallback bypassed the session-attached rich fallback
path that runs isRetryableModelSelectionError + swapPromptSession, so errors
like "No API key for provider: anthropic" propagated without trying the
configured planning fallback. Restore the dispatch with a WeakSet re-entry
guard that preserves the FN-4900 recursion fix for plugin-runtime sessions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 14:36:33 -07:00
gsxdsm
e4099f2ff5 Merge pull request #1063 from agustinsacco/feat/editable-agent-name
feat: make agent name editable in review step of creation modal
2026-05-25 14:21:45 -07:00
gsxdsm
ea2de3e3a7 Merge pull request #1057 from klarkc/fix/research-pipeline-htmlUrl-to-url
fix: Replace htmlUrl with url in gh search repos query
2026-05-25 14:21:28 -07:00
gsxdsm
65c04fa36a Merge pull request #937 from abeperl/feat/fn-024-add-dependencies-to-task-update
feat: Add optional dependencies parameter to fn_task_update tool
2026-05-25 14:18:01 -07:00
gsxdsm
fb3aa22e54 Merge pull request #918 from abeperl/fix/fn-009-task-done-missing-worktree
fix: allow fn_task_done when worktree directory missing
2026-05-25 14:13:31 -07:00
gsxdsm
629aa29b65 fix(dashboard): use fs.mkdir for cloudflared local-bin fallback on Windows
Replace execFileAsync("mkdir", ["-p", ...]) with fs.mkdir({ recursive: true })
in the cloudflared install fallback path. The -p flag is Unix-only and breaks
on Windows cmd.exe ("A subdirectory or file -p already exists"). Test mocks
updated to verify the fs.mkdir call instead of the shelled-out mkdir.

The original report covered both this site and packages/engine/src/worktree-hooks.ts;
the latter was already converted to fs.mkdir independently, so only the
dashboard route change is needed.

Co-Authored-By: kenlin8827 <kenlin8827@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 13:57:04 -07:00
gsxdsm
5fb4daf69e Merge branch 'main' into fix/fn-009-task-done-missing-worktree 2026-05-25 13:28:34 -07:00
gsxdsm
3a81e65e1d Merge branch 'main' into feat/fn-024-add-dependencies-to-task-update 2026-05-25 13:28:32 -07:00
gsxdsm
8650480622 Merge branch 'main' into fix/chat-sessions-multi-project-scope-clean 2026-05-25 13:28:29 -07:00
gsxdsm
6085e91295 Merge branch 'main' into fix/chat-view-message-truncation 2026-05-25 13:28:27 -07:00
gsxdsm
9d5ded9da5 Merge branch 'main' into fix/research-pipeline-htmlUrl-to-url 2026-05-25 13:28:23 -07:00
gsxdsm
56e3ee0454 Merge branch 'main' into feat/editable-agent-name 2026-05-25 13:27:40 -07:00
gsxdsm
1bd8f51198 fix(plugins/reports): wire @testing-library/react cleanup so React teardown doesn't leak past test env
The api stub from 43be32bbd silenced the fetch-rejection path but the suite
still failed on CI: the actual unhandled error is React's scheduler firing
deferred work via setImmediate after jsdom is torn down — its internal
render then dereferences `window` and throws ReferenceError. @testing-library
only auto-registers cleanup() when vitest `globals: true` is set, and this
package doesn't enable globals, so the React tree from each render() stays
mounted across teardown. Register cleanup manually in test-setup.ts so every
dashboard test unmounts its tree before the environment tears down.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:59:48 -07:00
gsxdsm
43be32bbd7 fix(plugins/reports): stub api in ReportsView test to prevent post-teardown unhandled error
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>
2026-05-25 12:47:39 -07:00
gsxdsm
88c465cfc0 fix(ci,engine): repair test sharding, case-variant ambiguity detection, post-merge CI
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>
2026-05-25 10:24:21 -07:00
Josemi Liebana
a3b6c1aab1 fix(dashboard): guard pagination state writes against stale-session race
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.
2026-05-25 12:35:19 +02:00
Josemi Liebana
7b90f441fe fix(test): address review — inherit coreSetup from root via extends: true
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.
2026-05-25 12:34:53 +02:00
Josemi Liebana
dc0b764977 fix(test): address review — inherit coreSetup from root via extends: true
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.
2026-05-25 12:34:46 +02:00
Josemi Liebana
ca0aa2ff9c fix(test): migrate fusion-plugin-reports vitest config from deprecated environmentMatchGlobs to test.projects
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.
2026-05-25 11:59:24 +02:00
Josemi Liebana
6fe7d6909c fix(test): migrate fusion-plugin-reports vitest config from deprecated environmentMatchGlobs to test.projects
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.
2026-05-25 11:59:18 +02:00
Josemi Liebana
435963d231 fix(context): trigger engine start as fire-and-forget in getProjectContext
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.
2026-05-25 03:13:16 +02:00
Agustin Sacco
bad675994a feat: make agent name editable in review step of creation modal 2026-05-24 04:57:53 -04:00
gsxdsm
0a6da9f4ce feat(FN-5583): handle notification deep links in useDeepLink hook
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
2026-05-24 00:11:08 -07:00
gsxdsm
d9055ee1a2 feat(FN-5582): add github tracking delete flow integration tests
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
2026-05-24 00:07:57 -07:00
gsxdsm
2d04cbe09d chore(release): v0.33.0
Version bump via changesets.
2026-05-23 23:12:21 -07:00
gsxdsm
8741ea0f56 chore: remove changeset referencing non-workspace package
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 23:10:23 -07:00
gsxdsm
ba72c27bbb chore: remove invalid changeset referencing non-workspace package
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 23:08:46 -07:00