perf(desktop): eliminate workspace tab-switch freeze via WorkspaceKeepAlive#2
Conversation
Add full Windows build pipeline for the Superset desktop app: Build infrastructure: - Cross-platform postinstall.ts replacing bash-only postinstall.sh - Fix copy-native-modules.ts: NTFS junction detection, two-step npm fetch (no shell pipe), GetCommitHash.bat shim for node-pty - Platform-aware runtime-dependencies.ts: skip macOS-only modules on Windows, always externalize for Rollup - Disable npmRebuild on Windows (Bun store path issue with node-pty) Runtime fixes: - Enable auto-updater on Windows - Windows shell selection in teardown.ts (COMSPEC/cmd.exe) - tree-kill for cross-platform process tree termination - cmd.exe and PowerShell support in shell-wrappers.ts - Remove win32 skip in terminal host socket test helper CI/CD: - Add build-windows job to build-desktop.yml - Add Windows .exe artifact handling to release-desktop.yml Tested: builds and runs on Windows 11 (x64), producing a working 265MB NSIS installer that launches and renders the login screen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Point auto-updater and electron-builder publish config to Sandman-Ren/superset instead of upstream superset-sh/superset - Add Windows .exe handling to canary release workflow - Update README: multi-platform downloads, Windows build instructions, ELv2-required modification notice, fork attribution Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adopt all 20 Windows compatibility patches for a fully functional Windows desktop experience: Build: - Switch node-pty to @lydell/node-pty for prebuilt Windows binaries - Fix Rollup TDZ with ELECTRON_RUN_AS_NODE banner - Strip crossorigin attributes on Windows (ASAR CORS fix) - Fix defineEnv to use || for empty string fallback - Materialize @lydell/node-pty platform binary from Bun store Terminal: - Named pipes (\.\pipe\) instead of Unix sockets on Windows - Fix double-nested dist/main path for daemon/worker scripts - Use \r instead of \n for ConPTY command execution - Ctrl+C copies selection / Ctrl+V pastes from clipboard - Fix WebGL renderer garbled text on initial open - Add SYSTEMDRIVE, PROGRAMDATA, PROCESSOR_ARCHITECTURE to env allowlist UI/UX: - Custom superset-app:// protocol for proper dynamic imports on Windows - CORS bypass for API requests from custom protocol origin - Sound playback via Chromium audio engine (replaces unreliable PowerShell) - Workspace shortcut display: Ctrl+Shift+ on Windows instead of ⌘ - Quit confirmation dialog before window closes (not after) - Forward renderer console messages for Windows debugging Apps: - Windows app resolution for Open In actions (VS Code, Cursor, Terminal, JetBrains IDEs) with LOCALAPPDATA/Programs and Toolbox path search Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…uperset-sh#2994) * Add Amp Code CLI support * Refactor agent registry and add custom agent CRUD groundwork * Use official Amp press kit logos * Use Amp square mark icon * Fix lint warning in agent settings test * Stop tracking repo Amp workspace settings * Lint * Use stdin for interactive Amp prompts * Trim repo-only Amp cleanup changes * Refactor agent prompt transport rendering * Unify builtin and custom agent models * Fix custom agent regressions and MCP precedence
Remove 7 workflows inherited from upstream that don't apply to this fork: - deploy-preview.yml, deploy-production.yml, cleanup-preview.yml (upstream infra) - release-desktop-canary.yml (cron-based canary releases) - generate-changelog.yml, update-docs.yml (cron-based Claude Code tasks) - triage-issue.yml (upstream issue management) Also removes orphaned .github/prompts/ files used by deleted workflows. Updates CI build job to run on all three platforms (ubuntu, macos, windows) to catch cross-platform regressions from Windows support changes. Adds sync-upstream.yml workflow to track upstream via daily PR creation. https://claude.ai/code/session_015Ws96Q8JoAgUBndmW6VfxH
superset-ai/superset → superset-sh/superset Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- build-desktop.yml: add `git config --system core.longPaths true` before checkout on Windows runner (260-char MAX_PATH causes checkout failures) - ci.yml: add dev branch to push trigger so CI runs on our working branch; add conditional long-paths step for Windows in build matrix - sync-upstream.yml: checkout dev branch and target dev as PR base so upstream syncs land directly in our working branch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adapts the Superset fork for Windows desktop builds and runtime behavior, while also refactoring agent/terminal integration (including Amp support) and aligning CI/workflows with the fork’s needs.
Changes:
- Adds Windows-focused desktop runtime/build updates (custom protocol loading, terminal-host IPC path handling, packaging adjustments, Windows UX tweaks).
- Refactors shared agent definitions/prompt launching (prompt transports, builtin agent registry, Amp preset/icon/docs) and extends MCP discovery.
- Cleans up upstream-only GitHub workflows and introduces fork-specific CI/upstream-sync automation.
Reviewed changes
Copilot reviewed 88 out of 91 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/postinstall.ts | New cross-platform postinstall for Bun (Windows-friendly). |
| README.md | Fork branding + Windows support/build notes. |
| packages/ui/src/assets/icons/preset-icons/index.ts | Registers Amp preset icon. |
| packages/ui/src/assets/icons/preset-icons/amp.svg | Adds Amp SVG asset. |
| packages/shared/src/builtin-terminal-agents.ts | Centralizes builtin terminal agents (includes Amp). |
| packages/shared/src/agent-prompt-launch.ts | Introduces prompt transport helpers (argv/stdin). |
| packages/shared/src/agent-definition.ts | New shared agent definition types + defaults. |
| packages/shared/src/agent-command.ts | Reworks agent commands to use builtin registry + transports. |
| packages/shared/src/agent-command.test.ts | Adds coverage for Amp prompt/file launch behavior. |
| packages/shared/src/agent-catalog.ts | Moves catalog to shared agent definitions; includes builtin agents. |
| packages/shared/package.json | Exposes new shared entrypoints (agent-definition / prompt-launch). |
| packages/mcp/src/tools/devices/start-agent-session/shared.ts | Improves supported-agents description generation. |
| packages/local-db/src/schema/zod.ts | Adds promptTransport + makes promptCommand optional for custom agents. |
| packages/chat/src/server/desktop/router/mcp-overview/mcp-overview.ts | Adds .amp/settings.json MCP server discovery. |
| packages/chat/src/server/desktop/router/mcp-overview/mcp-overview.test.ts | Tests .amp/settings.json discovery + precedence/fallback. |
| package.json | Switches postinstall to TS script (Bun-run). |
| bun.lock | Updates desktop version and switches to @lydell/node-pty. |
| apps/docs/content/docs/terminal-presets.mdx | Documents Amp terminal preset. |
| apps/docs/content/docs/mcp.mdx | Documents Amp MCP setup + config file support. |
| apps/docs/content/docs/agent-integration.mdx | Adds Amp to supported agents. |
| apps/desktop/vite/helpers.ts | Adds Windows HTML transform plugin; adjusts env define helper. |
| apps/desktop/test-setup.ts | Updates desktop test zod mocks for new agent fields. |
| apps/desktop/src/shared/utils/agent-settings.ts | Adds custom-agent CRUD helpers + prompt transports support. |
| apps/desktop/src/shared/utils/agent-settings.test.ts | Adds tests for Amp defaults + custom-agent helpers. |
| apps/desktop/src/shared/utils/agent-launch-request.test.ts | Verifies Amp launch commands use stdin transport. |
| apps/desktop/src/renderer/stores/tabs/terminal-callbacks.ts | Removes terminal restart callback plumbing. |
| apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx | Stops registering restart callback. |
| apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useTerminalLifecycle.ts | Simplifies attach cancellation on unmount. |
| apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/useTerminalLifecycle.test.ts | Removes tests for deleted attach-unmount helper; keeps throttle tests. |
| apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/hooks/attach-unmount.ts | Removes attach keepalive helper. |
| apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/helpers.ts | Windows WebGL refresh + Ctrl+C/Ctrl+V behavior tweaks. |
| apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx | Shows Windows-friendly shortcut label formatting. |
| apps/desktop/src/renderer/routes/_authenticated/settings/agents/components/AgentsSettings/components/AgentCard/AgentCard.tsx | Splits builtin preset edits vs custom-agent edits via new mutations. |
| apps/desktop/src/renderer/routes/_authenticated/settings/agents/components/AgentsSettings/components/AgentCard/agent-card.utils.ts | Allows clearing promptCommand for custom terminal agents. |
| apps/desktop/src/renderer/routes/_authenticated/settings/agents/components/AgentsSettings/components/AgentCard/agent-card.utils.test.ts | Adds tests for builtin vs custom promptCommand clearing. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/hooks/useWorkspaceRunCommand.ts | Removes restart-callback path; always relaunches via launch helper. |
| apps/desktop/src/renderer/lib/terminal/launch-command.ts | Uses \r EOL for command execution to support Windows ConPTY. |
| apps/desktop/src/renderer/index.html | Adds media-src CSP allowance. |
| apps/desktop/src/main/windows/main.ts | Adds renderer console forwarding + Windows quit confirmation dialog. |
| apps/desktop/src/main/terminal-host/test-helpers.ts | Lets socket probe determine support on Windows. |
| apps/desktop/src/main/terminal-host/index.ts | Switches to centralized terminal-host paths + Windows socket checks. |
| apps/desktop/src/main/lib/terminal/env.ts | Expands allowed env vars needed for Windows toolchains. |
| apps/desktop/src/main/lib/terminal/dev-reset.ts | Avoids deleting socket file on Windows; uses path constants. |
| apps/desktop/src/main/lib/terminal-host/paths.ts | Adds named-pipe/socket path abstraction for terminal host (Windows). |
| apps/desktop/src/main/lib/terminal-host/client.ts | Uses shared terminal-host paths; adjusts Windows socket existence logic. |
| apps/desktop/src/main/lib/notifications/server.test.ts | Expands event mapping test coverage (Codex variants). |
| apps/desktop/src/main/lib/notifications/map-event-type.ts | Maps more Codex event type variants to Start/Stop/PermissionRequest. |
| apps/desktop/src/main/lib/notification-sound.ts | Plays notification sounds via renderer audio on Windows. |
| apps/desktop/src/main/lib/auto-updater.ts | Enables Windows auto-update feed URLs for this fork. |
| apps/desktop/src/main/lib/agent-setup/templates/notify-hook.template.sh | Improves Codex event-type parsing for notify hook. |
| apps/desktop/src/main/lib/agent-setup/shell-wrappers.ts | Adds cmd/powershell argument handling and typed managed binary iteration. |
| apps/desktop/src/main/lib/agent-setup/index.ts | Routes setup through new desktop-agent setup orchestration. |
| apps/desktop/src/main/lib/agent-setup/desktop-agent-setup.ts | New orchestrator for desktop agent setup actions. |
| apps/desktop/src/main/lib/agent-setup/desktop-agent-capabilities.ts | Declares setup targets/actions and managed binaries (includes Amp). |
| apps/desktop/src/main/lib/agent-setup/agent-wrappers.ts | Exports Amp wrapper creator. |
| apps/desktop/src/main/lib/agent-setup/agent-wrappers.test.ts | Adds Amp wrapper tests + expands Codex hooks merge tests. |
| apps/desktop/src/main/lib/agent-setup/agent-wrappers-common.ts | Sources managed binaries list from capabilities module. |
| apps/desktop/src/main/lib/agent-setup/agent-wrappers-claude-codex-opencode.ts | Broadens Codex native hooks registration + stale-hook cleanup. |
| apps/desktop/src/main/lib/agent-setup/agent-wrappers-amp.ts | New Amp passthrough wrapper script generation. |
| apps/desktop/src/main/index.ts | Registers superset-app:// protocol + Windows CORS handling + quit behavior changes. |
| apps/desktop/src/lib/window-loader.ts | Uses superset-app:// loading path on Windows prod builds. |
| apps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.ts | Makes teardown shell selection + process-tree kill Windows-safe. |
| apps/desktop/src/lib/trpc/routers/settings/index.ts | Adds custom agent CRUD endpoints; uses default terminal preset list from shared. |
| apps/desktop/src/lib/trpc/routers/settings/agent-preset-router.utils.ts | Adds zod schemas + normalization for custom agent create/update. |
| apps/desktop/src/lib/trpc/routers/settings/agent-preset-router.utils.test.ts | Tests custom-agent schema validation + normalization. |
| apps/desktop/src/lib/trpc/routers/ringtone/index.ts | Plays/stops preview audio via renderer on Windows. |
| apps/desktop/src/lib/trpc/routers/external/helpers.ts | Adds Windows external app resolution (Program Files/Toolbox, etc.). |
| apps/desktop/src/lib/trpc/routers/changes/workers/git-task-runner.ts | Fixes direct dist/main execution worker script path lookup. |
| apps/desktop/scripts/rebuild-native-win.mjs | Adds Windows-only native module rebuild helper. |
| apps/desktop/scripts/copy-native-modules.ts | Improves Windows junction handling + safer npm tarball extraction + node-pty platform module materialization. |
| apps/desktop/runtime-dependencies.ts | Adds platform-gated runtime dependencies + extra asar unpack globs for node-pty. |
| apps/desktop/package.json | Switches node-pty to @lydell/node-pty; adds rebuild script. |
| apps/desktop/electron.vite.config.ts | Adds Windows env/process tweaks + strip-crossorigin plugin + banner tweak. |
| apps/desktop/electron-builder.ts | Points publishing to fork owner + disables npmRebuild on Windows. |
| apps/desktop/electron-builder.canary.ts | Points canary publishing to fork owner. |
| apps/desktop/docs/EXTERNAL_FILES.md | Documents Amp wrapper + updated Codex hooks strategy. |
| .gitignore | Ignores .amp/*. |
| .github/workflows/update-docs.yml | Removes upstream docs automation workflow. |
| .github/workflows/triage-issue.yml | Removes upstream issue triage automation workflow. |
| .github/workflows/sync-upstream.yml | Adds daily upstream sync PR workflow targeting dev. |
| .github/workflows/release-desktop.yml | Adds Windows artifact naming/copy logic. |
| .github/workflows/release-desktop-canary.yml | Removes upstream canary release workflow. |
| .github/workflows/generate-changelog.yml | Removes upstream changelog automation workflow. |
| .github/workflows/deploy-production.yml | Removes upstream production deploy workflow. |
| .github/workflows/cleanup-preview.yml | Removes upstream preview cleanup workflow. |
| .github/workflows/ci.yml | Adds dev branch push trigger + multi-OS build matrix + Windows long paths. |
| .github/workflows/build-desktop.yml | Adds dedicated Windows desktop build job + artifacts. |
| .github/prompts/update-docs.md | Removes upstream Claude docs prompt. |
| .github/prompts/triage-issue.md | Removes upstream triage prompt. |
| .github/prompts/generate-changelog.md | Removes upstream changelog prompt. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function defineEnv( | ||
| value: string | undefined, | ||
| fallback?: string, | ||
| ): string { | ||
| return JSON.stringify(value ?? fallback); | ||
| return JSON.stringify(value || fallback); | ||
| } |
There was a problem hiding this comment.
defineEnv now uses value || fallback, which treats an explicit empty string as “unset”. That can unexpectedly drop intentionally-empty env values and can also cause this function to return undefined at runtime (since JSON.stringify(undefined) returns undefined), despite the string return type. Prefer value ?? fallback (or value ?? fallback ?? "") so empty strings are preserved and the return value is always a string literal for Vite’s define map.
| import { dirname, join } from "node:path"; | ||
| import { dirname, join, resolve } from "node:path"; | ||
| import { tmpdir } from "node:os"; | ||
| import { satisfies } from "semver"; |
There was a problem hiding this comment.
writeFileSync is imported but never used. This will fail lint/typecheck in strict setups; please remove the unused import (or use it if intended).
| import { satisfies } from "semver"; |
| const SUPERSET_HOME_DIR = join(homedir(), SUPERSET_DIR_NAME); | ||
|
|
||
| const PIPE_SUFFIX = ( | ||
| process.env.USERNAME ?? process.env.USER ?? "user" | ||
| ).replace(/[^a-zA-Z0-9_.-]/g, "_"); | ||
|
|
||
| const SOCKET_PATH = IS_WINDOWS | ||
| ? `\\\\.\\pipe\\superset-terminal-host-${PIPE_SUFFIX}` | ||
| : join(SUPERSET_HOME_DIR, "terminal-host.sock"); |
There was a problem hiding this comment.
On Windows, SOCKET_PATH is currently \\.\pipe\superset-terminal-host-${PIPE_SUFFIX} and does not incorporate SUPERSET_DIR_NAME / workspace name (which is used elsewhere to isolate per-workspace state, e.g. .superset-<workspace>). This can cause terminal-host IPC collisions when running multiple Superset worktrees/workspaces on the same machine. Consider including a sanitized workspace identifier in the pipe name (and/or SUPERSET_HOME_DIR) to preserve the multi-worktree isolation guarantee.
| const IS_WINDOWS = process.platform === "win32"; | ||
|
|
||
| const SUPERSET_HOME_DIR = join(homedir(), SUPERSET_DIR_NAME); | ||
|
|
There was a problem hiding this comment.
This module hardcodes SUPERSET_HOME_DIR = join(homedir(), SUPERSET_DIR_NAME) instead of honoring process.env.SUPERSET_HOME_DIR (used elsewhere, e.g. teardown logic and app-environment). If SUPERSET_HOME_DIR is overridden for portability/testing, terminal-host would still read/write token/pid/lock files under the default home path. Consider basing SUPERSET_HOME_DIR on process.env.SUPERSET_HOME_DIR ?? join(homedir(), SUPERSET_DIR_NAME) for consistency.
… black screen GPU driver incompatibilities with Chromium's compositor can cause a completely black window on Windows. Extend the existing Linux GPU disable to Windows. Also add missing NEXT_PUBLIC_ELECTRIC_URL env var to macOS and Windows builds.
…switch freeze
Workspace switches caused a 2s+ full-window freeze because every navigation
unmounted and remounted the WorkspaceContent tree, forcing XTerm.js to
reinitialize (canvas setup + LigaturesAddon font measurement) for every
terminal pane.
Fix: introduce WorkspaceKeepAlive in DashboardLayout that keeps up to 5
recently-visited WorkspaceContent trees mounted simultaneously, toggling
display:none on inactive ones. Terminals are never destroyed or recreated
on workspace switch — only the first visit to each workspace pays the
initialization cost.
Supporting changes:
- WorkspaceIdContext: React context so multiple simultaneously-mounted
workspace trees each read their own workspace ID (not the URL's)
- WorkspaceContent: extracted all heavy workspace UI (terminals, hotkeys,
dialogs) from page.tsx into a standalone component accepting workspaceId
+ isActive props; hotkeys use { enabled: isActive } to prevent hidden
workspaces from firing shortcuts
- page.tsx: slimmed to route shell (loader guard + search-param tab
activation); adds staleTime: 30_000 to avoid IPC round-trip on warm
navigation
- usePresetHotkeys: added options param so preset hotkeys can be disabled
for inactive workspaces
- 10 workspace content files: migrated from useParams() to useWorkspaceId()
context hook so each mounted workspace reads its own ID, not the URL's
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In dev mode, Electron sets process.resourcesPath to the Electron binary's resource directory (inside node_modules/.bun/electron@.../dist/resources/). getMigrationsFolder() was unconditionally returning that path, which doesn't contain host-migrations during development, causing every migration to fail with "Can't find meta/_journal.json file". Fix: check existsSync on the production path before returning it. If it doesn't exist (dev mode), fall through to the HOST_MIGRATIONS_PATH env var or the import.meta.dirname source-relative fallback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove prevWorkspaceIdRef from WorkspaceKeepAlive — was declared and kept in sync but never read; display toggle is computed inline - Add runtime guard in useWorkspaceId fallback path: if called outside both a WorkspaceIdProvider and a workspace route, the useParams cast could silently return undefined despite the string return type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- vite/helpers.ts: fix defineEnv to use ?? instead of || so explicit empty strings are preserved; also guard against both args being undefined to avoid JSON.stringify returning undefined at runtime - copy-native-modules.ts: remove unused writeFileSync import - terminal-host/paths.ts: honour SUPERSET_HOME_DIR env var override (already used in teardown and app-environment) so token/pid/lock files resolve consistently when the env var is set Skipped: named-pipe per-worktree isolation (comment 3) — terminal-host is an intentional singleton per user, matching the Unix socket behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lint: - Reformat layout.tsx and WorkspaceIdContext.tsx to satisfy Biome's line length rules (long function signature and JSX props split to multi-line) Tests: - launch-command.test.ts, bootstrap-open-worktree.test.ts: update \n → \r in expected command output to match the \r EOL change in launch-command.ts (Windows ConPTY requires \r; the existing "already has newline" test correctly stays as \n since normalizeTerminalCommand passes it through) - shell-wrappers.test.ts: powershell is no longer unrecognized — it now returns ["-NoLogo"]. Move it out of the "unrecognized" case and add a dedicated "Windows shells" test covering cmd, powershell, and pwsh variants Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove unused `join` import (terminal-host/index.ts), prefix unused `findExistingPath` and `error` catch variable with underscore (helpers.ts, postinstall.ts), and fix single-quote formatting in electron.vite.config.ts. All errors pre-dated this branch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move useParams call before the early-return in useWorkspaceId to satisfy Rules of Hooks (useHookAtTopLevel). Apply Biome formatting and import ordering to remaining flagged files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Apply Biome's organizeImports fix to all files that import from WorkspaceIdContext (the import was added in the wrong position). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
display:nonewhen inactive) instead of unmounting and remounting themWhat was freezing and why
Every workspace navigation unmounted the previous workspace's React tree and mounted the new one. Each terminal pane synchronously ran
createTerminalInstance()on mount:With 2–4 panes open, this was 400ms–1600ms+ of synchronous main-thread work before the browser could paint — a complete UI freeze.
Fix: WorkspaceKeepAlive
WorkspaceKeepAliveinDashboardLayoutkeeps up to 5 recently-visited workspace content trees mounted simultaneously, togglingdisplay:noneon inactive ones. Terminals are never destroyed or recreated on switch — only the first visit to each workspace pays initialization cost.Supporting changes:
WorkspaceIdContext— React context so multiple simultaneously-mounted workspace trees each read their own workspace ID, not the URL'sWorkspaceContent— extracted all heavy UI (terminals, hotkeys, dialogs) frompage.tsxinto a standalone component; hotkeys use{ enabled: isActive }so hidden workspaces don't intercept shortcutsstaleTime: 30_000— warm navigations no longer block on an unnecessary IPC round-tripuseParams()touseWorkspaceId()context hookhost-service migration fix
In dev mode,
getMigrationsFolder()returnedprocess.resourcesPath + "/resources/host-migrations"— which resolves to inside the Electron binary's resources directory, not the project source. AddedexistsSyncguard so it falls through to the source-relative path when the production directory doesn't exist.Documentation
Full investigation report at
docs/issues/workspace-tab-switch-freeze.md.Test plan
host-serviceinitializes without migration errors🤖 Generated with Claude Code