What happened?
When the Electron main process rebuilds or restarts while a review is open, the renderer can continue displaying the previous review but approval, export, and review refresh operations fail.
The host stores the active workspace only in the process-local active variable in apps/desktop/src/electron/host.ts. A main-process restart clears that variable. The renderer can retain its existing DesktopReviewState, so the UI appears usable even though the replacement host has no open workspace.
A subsequent review.load fails with Open a workspace first. Approval and export similarly fail because review.dispatch and export.write resolve the stale workspace ID through workspaceFor.
Expected behavior: after a main-process restart, DraftLoop should either restore the previously opened local workspace safely or discard the stale review and present an actionable Open workspace recovery screen. It must not leave an apparently actionable review connected to a host with no workspace context.
Reproduction steps
-
Start the desktop app in development mode:
DRAFT_LOOP_ANTHROPIC_AUTH_MODE=api-key \\
DRAFT_LOOP_OPENAI_AUTH_MODE=user-session \\
pnpm --filter @draft-loop/desktop start
-
Open a workspace and run a review.
-
While the review remains rendered, trigger an Electron main-process rebuild/restart, for example by changing a main-process dependency during Forge/Vite development.
-
Observe that the renderer may continue showing the review.
-
Attempt to refresh, approve, or export.
Actual result: the operation fails and the terminal reports:
desktop host error (review.load): NativeHostError: Open a workspace first.
Immediate workaround: reload or restart the renderer, choose Open workspace, and select the existing workspace directory again.
Environment
- OS: Ubuntu 24.04 Linux
- Node: 24.5.0
- pnpm: 10.18.3
- Desktop package: 0.7.0-alpha.1
- Revision: 3ca5bfb
- Launch mode: Electron Forge with Vite development bundles
Relevant logs
[plugin-vite] target built src/electron/preload.ts
[plugin-vite] target built src/electron/main.ts
desktop host error (review.load): NativeHostError: Open a workspace first.
The Vite native-config/CommonJS warnings printed nearby appear unrelated.
Root cause
createNativeHost keeps the selected workspace only in a closure-local active variable.
workspace.open and workspace.create populate it, but a replacement main process starts with it unset.
- Renderer startup and polling call
review.load without a workspace identifier.
- Polling failures preserve the last successful review state and set only the inline operation error, creating a stale split-brain UI.
- Existing restart tests explicitly reopen the workspace before loading, so they do not cover host loss while renderer state survives.
Proposed fix
- Persist the most recently opened workspace root locally in the Electron main process, outside renderer-controlled IPC data.
- Restore and validate that root before the renderer performs its first
review.load after a main-process restart.
- If restoration is unavailable or invalid, treat a
not-found refresh/dispatch result as loss of workspace context: clear stale review state and show the workspace-opening recovery UI.
- Keep arbitrary filesystem paths out of renderer command input and preserve the native directory-selection boundary.
Acceptance criteria
- A main-process restart while a review is visible does not leave approval or export controls attached to stale host state.
- A valid previously opened workspace is restored safely, or the UI provides an actionable Open workspace recovery path.
- Approval and Markdown export work after recovery.
- Invalid, moved, or deleted remembered workspace paths fail safely and require explicit workspace selection.
- A regression test covers: open workspace → load review → recreate host/main lifecycle → recover → approve → export.
- Existing desktop host, bridge, and renderer tests continue to pass.
What happened?
When the Electron main process rebuilds or restarts while a review is open, the renderer can continue displaying the previous review but approval, export, and review refresh operations fail.
The host stores the active workspace only in the process-local
activevariable inapps/desktop/src/electron/host.ts. A main-process restart clears that variable. The renderer can retain its existingDesktopReviewState, so the UI appears usable even though the replacement host has no open workspace.A subsequent
review.loadfails withOpen a workspace first.Approval and export similarly fail becausereview.dispatchandexport.writeresolve the stale workspace ID throughworkspaceFor.Expected behavior: after a main-process restart, DraftLoop should either restore the previously opened local workspace safely or discard the stale review and present an actionable Open workspace recovery screen. It must not leave an apparently actionable review connected to a host with no workspace context.
Reproduction steps
Start the desktop app in development mode:
Open a workspace and run a review.
While the review remains rendered, trigger an Electron main-process rebuild/restart, for example by changing a main-process dependency during Forge/Vite development.
Observe that the renderer may continue showing the review.
Attempt to refresh, approve, or export.
Actual result: the operation fails and the terminal reports:
Immediate workaround: reload or restart the renderer, choose Open workspace, and select the existing workspace directory again.
Environment
Relevant logs
The Vite native-config/CommonJS warnings printed nearby appear unrelated.
Root cause
createNativeHostkeeps the selected workspace only in a closure-localactivevariable.workspace.openandworkspace.createpopulate it, but a replacement main process starts with it unset.review.loadwithout a workspace identifier.Proposed fix
review.loadafter a main-process restart.not-foundrefresh/dispatch result as loss of workspace context: clear stale review state and show the workspace-opening recovery UI.Acceptance criteria