The command-center token endpoint accepts groupBy=task, but the
dimension was never wired through: TokenGroupBy did not include "task",
groupKeyFor had no task case, VALID_GROUP_BY rejected it, and
groupAttributes emitted no attribute. As a result groupBy=task silently
fell back to ungrouped totals — the per-task rollup returned zero groups
even though tasks.tokenUsage* is populated per task.
Thread "task" through all four enumeration sites (the union type makes
the two switches compiler-exhaustive). Task rows group by task id; chat
rows have no task and return null, mirroring the existing node case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Require generated task specs to summarize the requested before-to-after transformation near the top.
- Add a Before → After Transformation section to standard and fast planning prompt templates.\n- Document the new task definition section and cover it with prompt regression tests.\n- Add a patch changeset for the published Fusion package.\n\nFiles changed:\n .changeset/fn-7499-before-after-transformation.md | 7 +++++++\n docs/task-management.md | 1 +\n packages/core/src/__tests__/agent-prompts.test.ts | 17 +++++++++++++++\n packages/core/src/agent-prompts.ts | 25 +++++++++++++++++++----\n packages/engine/src/__tests__/triage.test.ts | 20 ++++++++++++++++++\n 5 files changed, 66 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-7499
Fusion-Task-Lineage: d049b5d6-a4bc-40dd-831d-04a11f9dc2cf
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Show triage task cards when Plan Review is actively running.
- Render the existing progress affordance for triage cards only when unified progress has an active item.
- Keep enabled-but-idle workflow steps hidden to avoid false active indicators and empty progress shells.
- Cover running, idle, and empty triage progress states in TaskCard tests.
- Add a patch changeset for the published Fusion package.
Files changed:
.../FN-7492-task-card-plan-review-progress.md | 7 +++
packages/dashboard/app/components/TaskCard.tsx | 7 ++-
.../app/components/__tests__/TaskCard.test.tsx | 73 ++++++++++++++++++++++
3 files changed, 86 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7492
Fusion-Task-Lineage: 582aea40-8a14-4571-9a7c-e770bc2ab1fb
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Follow-up to #1883 (merged). The Windows Terminal "Help"/version dialogs
on Windows were **not** caused by the embedded terminal (already
guarded) — the real trigger is the **worktrunk integration**.
## Root cause
Worktrunk's CLI is named `wt` (`WORKTRUNK_BINARY_NAME = "wt"`), which
collides with **Windows Terminal** (`wt.exe`, an App Execution Alias
under `%LOCALAPPDATA%\Microsoft\WindowsApps`, on PATH by default on
Windows 11). Worktrunk resolution runs `where wt` → finds Windows
Terminal → runs `"wt.exe" --version` to probe it → **launches Windows
Terminal**, popping the native "Windows Terminal 1.24.11321.0" dialog.
This fired automatically because the Settings UI fetched
`/api/worktrunk/status` on mount even when worktrunk wasn't in use.
## Fix
1. **Don't probe worktrunk automatically** — `useWorktrunkInstallStatus`
only auto-fetches status when the integration is **enabled** (user
opt-in). A plain Settings/dashboard mount no longer probes.
2. **Engine invariant guard** — `probeWorktrunk` refuses to `exec` a
resolved `wt` that is the Windows Terminal alias, covering every
resolution surface (cached/override/PATH/install/settings-route).
Basename is computed host-independently so the guard holds on POSIX CI
hosts too.
3. The #1883 frontend terminal-auto-create guard is retained as
defense-in-depth.
## Tests
- `worktrunk-installer.test.ts` — `probeWorktrunk` returns `{ok:false}`
for a `WindowsApps\wt.exe` path **without** calling exec; still probes a
genuine `wt` elsewhere; `resolveWorktrunkBinary` never execs `--version`
against a Windows Terminal PATH hit.
- `useWorktrunkInstallStatus.test.ts` — no fetch on mount unless
`enabled`.
Report updated with corrected root cause + Symptom Verification +
Surface Enumeration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Resolved an issue where Windows Terminal “Help” dialogs could appear
when opening the dashboard or Settings on Windows.
* Worktrunk status checks are now gated behind integration enablement
and re-verified on save when turning it on.
* Added an engine safeguard to prevent probing/launching the Windows
Terminal alias during version checks.
* **Tests**
* Expanded coverage for Windows Terminal collision detection and for
opt-in behavior (including enablement toggles) in the status hook.
* **Documentation**
* Updated desktop release notes with the corrected root cause,
mitigations, and verification details.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Resolves the two failures from the full-suite run on `main` ([run
28697507894](https://github.com/Runfusion/Fusion/actions/runs/28697507894)).
### 1. dashboard `session-reconnect.test.ts` — real bug (deterministic)
The planning *"replays buffered events and supports reconnect catch-up"*
test hung at the 15s timeout.
**Root cause:** FN-7444 (planning summary deepening checkpoint) now
holds the completed planning summary behind a mandatory *"Would you like
to go deeper?"* checkpoint question instead of finalizing when the agent
returns a `"complete"` payload. `continueAgentConversation` calls
`setPendingSummaryCheckpoint`, which sets `session.pendingSummary` + a
checkpoint question and leaves `session.summary` undefined. The SSE
stream route's summary-emit path is therefore never reached, so it
subscribes waiting for a `"complete"` event that never arrives → 15s
hang.
**Fix:** respond to the deepening checkpoint with the reserved proceed
option (`PLANNING_DEEPEN_PROCEED_OPTION_ID`) so `finalizePendingSummary`
runs, `session.summary` is set, and the summary/complete events are
buffered for SSE replay.
- Reproduced locally: 15s timeout before, 4/4 green after.
- `routes-planning.test.ts`, `session-persistence-roundtrip.test.ts`,
`session-reconnect.test.ts` → 116/116 green.
### 2. cli `extension.test.ts` — loaded-lane CI flake (quarantined on
sight)
The built-dist-barrel `fn_task_list` test timed out at 5000ms in shard
4/4 while passing locally (~1.2s body) and in 3 of the 4 surrounding CI
runs.
**Root cause:** in-test dist-barrel recompilation (`vi.resetModules` +
`vi.importActual` of the full `@fusion/core` dist barrel + a fresh
dynamic `import("../extension.js")`) inside the default 5s test timeout.
That work is CPU-bound and degrades non-linearly under 4-shard CI
contention — the same loaded-lane signature rescued in FN-6483 / FN-6705
/ FN-6795 / FN-6839.
**Action:** quarantined on sight per the *Flaky Tests Are Quarantined on
Sight* rule — ledger entry in `scripts/lib/test-quarantine.json` plus
the matching `exclude` in `packages/cli/vitest.config.ts`, in the same
commit. No widened timeout, no retries, no loosened assertions (all
forbidden by the rule). The sibling source-`@fusion/core` test *"bounds
large column-filtered listings"* covers the identical truncation
invariant through source, so the dist-barrel slice's marginal coverage
is dist-resolution, which has been stable.
⚠️ **Collateral:** the file-granular exclude also drops the ~68
otherwise-stable tests in `extension.test.ts` until a rescue before the
2026-07-18 deletion deadline. This matches the project's quarantine
ratchet; the file has been rescued 4× before.
## Verification
- `packages/dashboard` `session-reconnect.test.ts` → 4/4 ✅
- `packages/dashboard` `routes-planning` +
`session-persistence-roundtrip` + `session-reconnect` → 116/116 ✅
- `pnpm test:gate` (engine-core 321 + ci-shape 63) → ✅
- `pnpm test:gate` appeasement-check + changeset-format-check → ✅ (no
changeset needed: test-only changes to a private package)
- cli `extension.test.ts` confirmed excluded post-quarantine.
## Notes
- No changeset: both changes are test-only and don't affect published
`@runfusion/fusion` behavior.
- No production code changed.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved test reliability by keeping a known flaky CLI test out of the
main run, reducing CI timeout-related failures.
* Updated reconnect behavior coverage to better match event replay
expectations after planning updates.
* **Tests**
* Adjusted dashboard session reconnect tests to validate the full
reconnect-and-replay flow more accurately.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Two failures surfaced in the full-suite run on main (28697507894):
1. dashboard session-reconnect.test.ts — real bug. The planning
"replays buffered events" test hung at the 15s timeout because
FN-7444 (planning summary deepening checkpoint) now holds the
completed summary behind a mandatory checkpoint question instead
of finalizing on the agent's "complete" payload. The stream route
never observed session.summary and subscribed forever. Fix: respond
to the deepening checkpoint with the reserved proceed option so
finalizePendingSummary runs, session.summary is set, and the
summary/complete events are buffered for SSE replay. Reproduced
locally (15s hang) and verified green (4/4).
2. cli extension.test.ts — loaded-lane CI flake. The built-dist-barrel
fn_task_list test timed out at 5000ms under 4-shard contention while
passing locally (~1.2s body) and in 3 of the 4 surrounding runs.
Root cause is in-test dist-barrel recompilation inside the default
5s timeout (vi.resetModules + vi.importActual of the full core dist
+ fresh dynamic import), the same signature rescued in
FN-6483/FN-6705/FN-6795/FN-6839. Quarantined on sight per the
flaky-test rule (ledger + matching vitest exclude) rather than
widening the timeout or loosening assertions; the sibling
source-@fusion/core test covers the identical truncation invariant.
## Summary
Fixes three root causes behind CI run
[28695362549](https://github.com/Runfusion/Fusion/actions/runs/28695362549)
failures on main.
### 1. droid-cli: missing `@fusion-plugin-examples/droid-runtime` alias
(8 tests)
The droid-cli `index.test.ts` imports
`@fusion-plugin-examples/droid-runtime`, but the droid-cli vitest config
had no source alias. Without the alias, Vite tries to resolve the
package's dist/ exports which don't exist in a source checkout, causing
every droid-cli test that touches the droid runtime to fail.
**Fix:** Added `resolve.alias` entries for
`@fusion-plugin-examples/droid-runtime` and `/probe` subpath to
`packages/droid-cli/vitest.config.ts`.
### 2. CLI: missing `@fusion-plugin-examples/roadmap` aliases
The CLI imports the roadmap plugin (`roadmap-routes.ts`,
`roadmap-suggestions.ts`), but the CLI vitest config was missing source
aliases for `@fusion-plugin-examples/roadmap` and its `/server` and
`/roadmap-suggestions` subpaths.
**Fix:** Added three regex aliases to `packages/cli/vitest.config.ts`.
### 3. CLI: missing `runTaskImportFromGitLab` mock export (73 tests)
The GitLab task import command was added to `bin.ts` and
`commands/task.ts` but `bin.test.ts`'s `vi.mock("../commands/task.js")`
factory was not updated to include the new export, causing 73 tests to
fail with `No "runTaskImportFromGitLab" export is defined on the
"../commands/task.js" mock`.
**Fix:** Added `runTaskImportFromGitLab: vi.fn()` to the hoisted
`commandMocks` and the mock factory in
`packages/cli/src/__tests__/bin.test.ts`.
## Verification
- droid-cli: 232 passed
- CLI bin.test.ts: 74 passed
- CLI broader tests (11 files): 146 passed, 90 skipped
- CLI dashboard-tui tests: 72 passed
- Gate suite (`pnpm test:gate`): 319 engine-core + 63 CI-shape passed
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved test environment module resolution so local source packages
load correctly during CLI and Droid CLI test runs.
* Added support for additional command routing in test coverage, helping
import-related flows dispatch to the right handler.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- probeWorktrunk: also refuse a bare wt/wt.exe override on Windows (resolves to
Windows Terminal via PATH), and tighten the package-dir match from a broad
'windowsterminal' substring to 'microsoft.windowsterminal' so a genuine
worktrunk under an unrelated *windowsterminal* folder is still probed.
- Fix worktrunk enable deadlock/save-race: probe status when the user views the
Worktrees section (not gated on 'enabled', which deadlocked since the toggle is
disabled until status==installed), and re-verify on Save so a fast enable+save
can't silently persist enabled:false. Hook 'refresh' now returns the fetched
status and is exposed.
- Tests: bare-wt refusal, forward-slash Windows Terminal path, unrelated
windowsterminal-folder is probed, and a rerender enabled false->true probe-once
transition; update SettingsModal mocks for the new refresh().
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 1882/1883 terminal-auto-create guard fixed the wrong subsystem; the
embedded terminal was already guarded. The actual trigger: worktrunk's CLI is
named 'wt', colliding with Windows Terminal (wt.exe) on PATH, so probing it with
'wt --version' launched Windows Terminal and popped its native version dialog —
fired automatically by the Settings worktrunk-status fetch on mount.
- useWorktrunkInstallStatus: only auto-fetch /api/worktrunk/status when the
integration is enabled (user opt-in), never on a plain Settings/dashboard mount.
- probeWorktrunk: refuse to exec a resolved 'wt' that is the Windows Terminal
alias (WindowsApps / WindowsTerminal package dir), covering every resolution
surface (cached/override/PATH/install/settings-route). Basename computed
host-independently so the guard holds when the build host is POSIX.
- Tests for both guards; report updated with corrected root cause + Symptom
Verification + Surface Enumeration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three root causes behind CI run 28695362549 failures:
1. droid-cli: 8 tests fail with 'Failed to resolve entry for
@fusion-plugin-examples/droid-runtime'. The droid-cli vitest config
had no source alias for the droid-runtime plugin, so Vite tried to
resolve non-existent dist/ exports.
2. CLI: multiple tests fail with 'Failed to resolve entry for
@fusion-plugin-examples/roadmap'. The CLI vitest config was missing
source aliases for the roadmap plugin (., /server, /roadmap-suggestions).
3. CLI: 73 bin.test.ts tests fail with 'No runTaskImportFromGitLab export
is defined on the ../commands/task.js mock'. The GitLab import command
was added to bin.ts and task.ts but the bin.test.ts mock was not
updated to include the new export.
Keep the task-detail Activity view picker usable during mobile iOS opening taps.
- Add a short visualViewport opening guard for the root-portaled Activity views menu while preserving normal close behavior afterward.
- Reset menu position and guard state consistently on outside click, Escape, selection, task changes, and tab toggles.
- Cover the iOS visualViewport echo, portal/CSS contract, and documentation plus a patch changeset.
Files changed:
.changeset/activity-menu-ios.md | 7 +
docs/dashboard-guide.md | 3 +-
.../dashboard/app/components/TaskDetailModal.tsx | 68 +++++++---
.../__tests__/TaskDetailModal.css.test.ts | 9 +-
.../TaskDetailModal.task-activity-chat.test.tsx | 141 ++++++++++++++++++++-
5 files changed, 210 insertions(+), 18 deletions(-)
Fusion-Task-Id: FN-7485
Fusion-Task-Lineage: 291331c4-453c-481b-b991-41f57db23e3b
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Treat desktop embedded engine managers as automation-capable during startup so the dashboard avoids false remediation.
- Report process-level engine availability when a modern manager can lazily start or is starting project engines.\n- Keep project-scoped status details on /api/engine/status while hiding the dashboard-only unavailable banner in desktop mode.\n- Add dashboard health polling, banner, server health, and changeset coverage for the desktop false-banner regression.\n\nFiles changed:\n .changeset/fn-7476-desktop-engine-banner.md | 7 +++++++\n .../dashboard/__tests__/DashboardBanners.test.tsx | 18 ++++++++++++++++\n .../app/hooks/__tests__/useDashboardHealth.test.ts | 24 +++++++++++++++++++++-\n packages/dashboard/src/__tests__/server.test.ts | 24 ++++++++++++++++++----\n packages/dashboard/src/server.ts | 9 +++++---\n 5 files changed, 74 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-7476
Fusion-Task-Lineage: d54a9872-252b-44c5-9c6d-6168b65118c7
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Use packaged runtime metadata so desktop update checks do not compare npm releases against an unresolved current version.
- Resolve @fusion/desktop package metadata as a fallback when dashboard code runs from a desktop deployment.
- Fail closed when the current version is the 0.0.0 sentinel in both update-check paths.
- Cover CLI, desktop, and unresolved-version update-check behavior with regression tests.
- Add a patch changeset for the published Fusion CLI package.
Files changed:
.changeset/fn-7471-desktop-update-version.md | 7 ++
.../src/__tests__/cli-package-version.test.ts | 59 +++++++++++++++-
.../src/__tests__/update-check-route.test.ts | 78 +++++++++++++++++++++-
.../dashboard/src/__tests__/update-check.test.ts | 15 +++++
packages/dashboard/src/cli-package-version.ts | 37 +++++++++-
packages/dashboard/src/server.ts | 12 +++-
packages/dashboard/src/update-check.ts | 15 +++++
7 files changed, 216 insertions(+), 7 deletions(-)
Fusion-Task-Id: FN-7471
Fusion-Task-Lineage: ee633b8c-766b-43f3-855d-3ce7abd41550
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
## Summary
Fixes 48 dashboard test files failing in CI run
[28681317913](https://github.com/Runfusion/Fusion/actions/runs/28681317913)
with `Failed to resolve entry for package
"@fusion-plugin-examples/cursor-runtime"`.
## Root cause
`runtime-provider-probes.ts` imports `probeCursorBinary` from
`@fusion-plugin-examples/cursor-runtime`, but neither the dashboard nor
CLI vitest configs had source aliases for this package. The
cursor-runtime plugin was added (with `runtime-provider-probes.ts`
consuming it) without updating the vitest configs to resolve the package
to its TypeScript source entry points.
Without the aliases, Vite tries to resolve the package's
`exports.import` field (`dist/index.js`) which doesn't exist in a source
checkout, causing every dashboard test that transitively imports the
runtime provider to fail at import time.
## Fix
Added `@fusion-plugin-examples/cursor-runtime` and
`@fusion-plugin-examples/cursor-runtime/probe` source aliases to:
- `packages/dashboard/vitest.config.ts`
- `packages/cli/vitest.config.ts`
This mirrors the existing pattern for droid-runtime, hermes-runtime,
openclaw-runtime, and paperclip-runtime.
## Verification
- Previously-failing dashboard tests now pass (295+ tests across 10
sampled files)
- Merge gate (`pnpm test:gate`) passes: 319 engine-core + 63 CI-shape
tests green
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved test setup so the dashboard can load the cursor runtime
directly from source, preventing missing export errors in local
checkouts.
* Added support for resolving the cursor runtime’s probe entry during
test runs, avoiding Vite resolution failures.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
runtime-provider-probes.ts imports probeCursorBinary from
@fusion-plugin-examples/cursor-runtime, but neither the dashboard nor CLI
vitest configs had source aliases for this package. Without the aliases,
Vite tried to resolve the package's dist/ exports which don't exist in a
source checkout, causing every dashboard test that transitively imports
the runtime provider to fail with 'Failed to resolve entry for package'.
This broke 48 dashboard test files in CI run 28561416741 after the cursor
runtime plugin was added without updating the vitest configs.
Two first-run desktop bugs.
## 1. "Agent with this name already exists" when creating the default
CEO
First-time project setup surfaced a **blocking** error when creating the
default **CEO** agent. The default first agent can be created from more
than one first-run surface (the unified `ModelOnboardingModal` agent
step **and** the project-setup `SetupWizardModal` sub-flow), and agent
names are unique per store (`agent-store.ts` `findAgentByName`), so the
second create returns 409.
The step's goal — *a first agent exists* — is already satisfied, so
**both** onboarding surfaces now treat a name collision as success and
advance instead of blocking. The user still creates the agent; they just
aren't punished for the flow offering it twice.
- `ModelOnboardingModal.tsx` `handleCreateFirstAgent`
- `SetupWizardModal.tsx` `handleCreateFirstAgent`
## 2. Header "Desktop local mode / Switch server" button did nothing
Clicking it should open the connection menu to switch between running
locally, connecting to a remote server, or a different remote.
Root cause: main relays the click as a `shell:open-connection-manager`
IPC (`ipc.ts` → `webContents.send`), but the **preload never forwarded
it** to the `window` DOM event `ShellContext.tsx` listens for
(`window.addEventListener("shell:open-connection-manager")`) — so the
signal was dropped and `NativeShellConnectionManager` never opened.
Fix: add the preload bridge (`preload.ts`) that re-dispatches the IPC as
the window event. The modal it opens already provides the Local/Remote
toggle (`setDesktopMode`) plus remote-server profile add/switch —
exactly the requested menu.
## Tests
- `SetupWizardModal.test.tsx`: advances to completion (no error alert)
when `createAgent` rejects with "already exists".
- `preload.test.ts`: the `shell:open-connection-manager` IPC handler
dispatches the `window` event.
## Surfaces enumerated
Both onboarding agent-create surfaces handled (ModelOnboarding +
SetupWizard). The conversational `AgentOnboardingModal` is intentionally
left to surface genuine duplicate errors (it creates user-named agents,
not the first-run default).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved onboarding so if the first agent already exists, setup
continues instead of showing an error.
* Fixed the desktop “Switch-server” button so it reliably opens the
connection manager.
* **Tests**
* Added coverage for the onboarding flow when the agent already exists.
* Added coverage to verify the desktop connection-manager event is
forwarded correctly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
First-run project setup surfaced a blocking "Agent with this name already exists" error when creating
the default CEO. The default first agent can be created from more than one first-run surface (the
unified ModelOnboarding agent step and the project-setup SetupWizard sub-flow), and agent names are
unique per store, so the second create returned 409. The step's goal — a first agent exists — was
already satisfied, so both onboarding surfaces now treat a name collision as success and advance
instead of blocking. The user still creates the agent; they just aren't punished for the flow offering
it twice.
Separately, the desktop header "Desktop local mode / Switch server" button did nothing: main relays
the click as a `shell:open-connection-manager` IPC (webContents.send), but the preload never forwarded
it to the `window` DOM event ShellContext listens for, so the signal was dropped. Add the preload
bridge so the button opens NativeShellConnectionManager (Local/Remote toggle + remote-server profiles),
letting operators switch between running locally, connecting to a remote server, or a different remote.
Regression tests: SetupWizard advances on an already-exists agent; preload dispatches the window event
on the IPC.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
## Problem
The Windows desktop installer can't package from `main`.
electron-builder's production-dependency walk rejects the staged deploy
closure:
```
⨯ Production dependency @aws-sdk/core not found for package @aws-sdk/credential-provider-env version=^3.974.27
```
**Root cause:** an incidental `pnpm.overrides` entry — `"@aws-sdk/core":
"3.974.26"` — added without rationale in an unrelated commit
(`91fb53f3c`, "add agent update tool"). The bundled Fusion agent
packages later pulled `@aws-sdk/*` versions requiring `^3.974.27`, but
the exact-version override force-held core at `.26`. Classic
stale-exact-pin trap. It only surfaced at release/local-build time
because **nothing in the merge gate validates the packageable closure**
— electron-builder's walk ran only in `workflow_dispatch`/release
workflows.
## Fix (item 1)
Remove the `@aws-sdk/core` override. The `--legacy` deploy closure then
resolves core to `3.974.27` (satisfies all consumers). The main lockfile
still resolves core to `3.974.26` for its own internally-consistent
graph, so the **published `@runfusion/fusion` closure is unchanged** (no
changeset needed).
**Verified locally:** a fresh `pnpm --filter @fusion/desktop build` +
`electron-builder` passes the dependency walk and produces the installer
with **no manual patching** (previously required hand-editing the
deploy's core version).
## Prevention (items 2 + 3)
New **advisory, path-gated `Desktop packaging`** job in `pr-checks.yml`:
- `pnpm dedupe --check` — early-warning for lockfile version drift
(non-fatal).
- `pnpm --filter @fusion/desktop build` + `electron-builder --dir` —
reproduces the authoritative production-dependency walk (skips
NSIS/signing for speed; the walk that catches skew is
platform-independent, so ubuntu suffices).
- Path-gated to `pnpm-lock.yaml` / `package.json` /
`pnpm-workspace.yaml` / `packages/{desktop,dashboard,engine,core}` /
`plugins/` so it only runs when the closure can change.
So any future dependency skew — for **any** dependency — fails at PR
time instead of at release.
### Why not required / not in `full-suite.yml`
Kept **out of the required set** so the thin merge gate stays exactly
`[Lint, Typecheck, Build, Gate]` and branch protection is untouched (per
the gate's design docs). It reports a status on PRs; promote to
merge-blocking by adding "Desktop packaging" to the repo's required
checks. Placed in `pr-checks.yml` (not `full-suite.yml`) so it runs at
**PR time** rather than only post-merge.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Added an extra, PR-only non-blocking desktop packaging validation when
relevant project files change.
* The check builds the desktop package and verifies production
dependency consistency earlier in the review process.
* Updated dependency override settings by removing an outdated version
pin while keeping other pinned packages unchanged.
* Improved workflow comments to clarify how merge-blocking status checks
are handled.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
The desktop-pack job broke the CI-shape invariant (ci-workflow.test.ts) that
pr-checks.yml contains exactly [build, gate, lint, typecheck] — the gate's
job set maps 1:1 to branch-protection required checks. Extract the advisory
desktop-packaging validation into desktop-packaging.yml, still PR-triggered
and non-required, so the thin gate stays pure.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Windows desktop installer failed to package: electron-builder's production-dependency walk rejected
`@aws-sdk/core@3.974.26` because `@aws-sdk/credential-provider-env` (resolved in the `--legacy` deploy
closure) requires `^3.974.27`. Root cause: an incidental `pnpm.overrides` entry pinning
`@aws-sdk/core` to the exact version `3.974.26` (added without rationale in an unrelated commit) which
force-held core below what its consumers now demand — the classic stale-exact-pin trap.
Fixes / prevention:
- Remove the `@aws-sdk/core` override so the deploy closure resolves core to 3.974.27 (satisfies all
consumers). The main lockfile still resolves core to 3.974.26 for its own consistent graph, so the
published @runfusion/fusion closure is unchanged (no changeset needed). Verified locally: a fresh
`@fusion/desktop build` + `electron-builder --dir` now passes the dependency walk with no manual patch.
- Add an advisory, path-gated `Desktop packaging` job to pr-checks.yml that reproduces electron-builder's
production-dependency walk (`--dir`, no NSIS/signing) plus a `pnpm dedupe --check` early-warning. This
is the only check that validates the packageable closure, which previously ran only in release/manual
workflows — so any future dependency skew now fails at PR time, for ANY dependency, instead of at
release/local-build time. Kept OUT of the required set so the thin merge gate [Lint, Typecheck, Build,
Gate] and branch protection are untouched; promote to blocking by adding it to required checks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace real-timer waits with a deterministic bounded microtask flush for
the fire-and-forget task lifecycle hook chain (FN-5048: no slow tests).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to #1878 (merged). While building the Windows desktop
installer locally, `pnpm deploy`'s final temp-dir rename hits `EPERM` on
the orca-managed Windows workspace mount and litters deeply-nested
`deploy_tmp_*` dirs that are painful to remove.
This lets local Windows installer builds redirect the staged production
closure to an external, plain-NTFS path via a new
`FUSION_DESKTOP_DEPLOY_DIR` env var. **CI and the default in-tree
`packages/desktop/deploy` path are unchanged** — the override is opt-in.
Verified locally: with
`FUSION_DESKTOP_DEPLOY_DIR=C:\Users\...\fusion-build\deploy`, `pnpm
--filter @fusion/desktop build` stages the full closure externally
without the EPERM race, and `electron-builder --projectDir <that dir>
--win nsis --x64` produces a signed installer.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Desktop deployment staging can now be configured with an environment
variable, making it easier to customize where build files are prepared.
* **Documentation**
* Added notes about updated desktop deployment behavior on Windows,
including common rename and cleanup issues.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This PR is a field report from testing the Fusion 0.52.0 Windows desktop
release. It does not claim to be a final fix; instead it documents every
regression we hit, what we tried, and what we think the Fusion team
should do.
## Issues covered
1. `fusion desktop` fails to launch on Windows because the published
`@runfusion/fusion` package lists `electron` as a devDependency, so
`require('electron')` fails after a global install.
2. The native desktop launcher walks ancestor directories looking for a
workspace and crashes on unrelated JSON.
3. **Manage Projects** opens Settings instead of the project overview
(also fixed in #1882).
4. Windows Terminal native `Help` version dialogs (`Windows Terminal
1.24.11321.0`) appear on dashboard load / Settings page (also fixed in
#1882).
5. Packaged desktop release can be missing `preload.cjs` in the unpacked
Electron resources path.
6. Dashboard/gateway ports drift on Windows and collide with other
instances.
7. GPU/sandbox rendering instability on Windows Electron.
8. Desktop user-data is not isolated under `~/.fusion/`.
9. `fusion desktop` does not reuse an already-running dashboard server.
## Files
- `reports/desktop-release-issues-2026-07-03.md` — full report with
symptoms, root causes, what we tried, and proposed fixes.
- `packages/cli/src/commands/desktop.ts` — experimental Windows
GPU/sandbox Electron flags we used locally.
- `packages/desktop/src/main.ts` — experimental `FUSION_SERVER_PORT`
reuse path we used locally.
## Context
Tested on Windows 11 (build 26200) with Fusion 0.51.0 (last known good)
and 0.52.0 (current release with regressions). We also built a
standalone wrapper at
`https://github.com/Automata-intelligentsia/fusion-desktop-windows/releases/tag/v1.1.0`
that works around the native desktop problems by running `fn dashboard
--no-auth` and loading it in Electron.
## Recommendations
- Move `electron` to runtime dependencies in the published CLI package.
- Add Windows CI that launches the packaged desktop and asserts the
window is visible/responsive.
- Verify the packaged `release/win-unpacked` layout includes
`preload.cjs` before publishing.
- Consider the wrapper’s CLI-server + Electron-shell model as an
official fallback for Windows until the native desktop build is stable.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed Fusion Desktop startup for published installs by bundling the
Electron runtime dependency.
* Improved Windows stability (including Windows-only GPU/sandbox-related
Electron flags and skipping frontend terminal auto-creation).
* Prevented desktop/server startup conflicts by reusing the existing
dashboard via `FUSION_SERVER_PORT` and gating embedded local startup.
* Added one-time desktop profile migration to a dedicated user-data
location and improved task view routing consistency.
* Added safeguards to avoid shipping incomplete Electron desktop
packages (required assets must be present).
* **Documentation**
* Added/updated a Windows desktop regression field report with
resolution status.
* **Tests**
* Added coverage for the new user-data migration behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->