- root cause: window-all-closed called killBackend() (windows: taskkill /F) BEFORE before-quit could POST /shutdown-all, so the backend died before running stop_all(), orphaning the bundled vite node.exe; a running node.exe locks its own image at resources\node\x64\node.exe, so the next nsis upgrade cannot overwrite it and surfaces appCannotBeClosed via the file-lock retry path (extractAppPackage.nsh / installUtil.nsh), not the name-based app-running check
- windows-only because mac's killBackend sends SIGTERM, letting uvicorn run its lifespan shutdown -> stop_all(); only since 1.1.64 because app builder first spawned these children on windows in bf6d7a9 (before that they died with WinError 2)
- electron/main.js: drop the premature killBackend() in window-all-closed (will-quit still kills the backend, now AFTER the reap) and raise the pre-quit reap budget from 2s to 10s to cover stop_all's parallel taskkill (up to 5s) + 3s grace
- electron/build/installer-recovery.nsh: replace the customInit orphan-killer (wmic, removed from windows 11 24H2 so it silently no-oped) with a path-scoped powershell Stop-Process; backtick-delimited for valid nsis tokenization, verified the macro compiles with makensis + the bundled nsExec plugin
- mac unaffected: will-quit already kills the backend; the app reap just runs explicitly via /shutdown-all now instead of riding on SIGTERM timing; the .nsh path is windows-only
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- root cause: window-all-closed called killBackend() (windows: taskkill /F) BEFORE before-quit could POST /shutdown-all, so the backend was dead before it could run stop_all(), orphaning the bundled vite node.exe; a running node.exe locks its own image at resources\node\x64\node.exe, so the next nsis upgrade cannot overwrite it and surfaces appCannotBeClosed via the file-lock retry path (extractAppPackage.nsh / installUtil.nsh), not the name-based app-running check
- windows-only because mac's killBackend sends SIGTERM, which lets uvicorn run its lifespan shutdown -> stop_all(); only since 1.1.64 because app builder first started spawning these children on windows in bf6d7a9 (before that they died with WinError 2)
- fix: drop the premature killBackend() in window-all-closed (will-quit still kills the backend, now AFTER the reap) and raise the pre-quit reap budget from 2s to 10s to cover stop_all's parallel taskkill (up to 5s) + 3s SIGTERM grace
- mac unaffected: will-quit already kills the backend; the app reap simply runs explicitly via /shutdown-all now instead of riding on SIGTERM timing
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- installation_id was created lazily on the first analytics submission; 1.1.64 removed the workflows startup poller, which was the early backend ping that used to generate it, so on a clean install the sign-in window built its google/email oauth url with an empty install_id and the cloud rejected the start
- now generated in main.py at the same pre-bind moment as the auth token, so GET /api/settings carries it from the very first fetch (no dependency on analytics timing)
- idempotent uuid4 hex, only written when missing; lazy path kept as fallback; platform-agnostic so mac is unchanged except the id exists slightly earlier
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>