fix(desktop): persist zoom to JSON, save window state on show, auto-relaunch --no-sandbox on Windows crash loop - #57414
Conversation
…elaunch with --no-sandbox on Windows crash loop (NousResearch#56726)
23bf322 to
352d289
Compare
…sandbox loss Follow-up to the salvaged #66803 (@HexLab98): - Two-strike boot marker: a single mid-boot abort (task-manager kill, power loss) no longer disables the sandbox — only a second consecutive abort, or a signature-confirmed GPU/renderer STATUS_BREAKPOINT death, engages --no-sandbox. - Version-scoped stickiness: the fallback marker records the app version and re-probes the sandbox once after an update (new Electron or installer ACL repair may have fixed the host) instead of degrading forever. A failed re-probe returns straight to fallback. - Launch-time icacls repair now runs only when the marker shows a prior aborted boot (icacls /T recurses the whole install tree — healthy launches skip it; the installer grants the ACE at install time), and targets the install dir only. The userData grant is dropped: granting S-1-15-2-2 RX on userData would expose Hermes sessions/config to every AppContainer app on the machine. - Renderer crash-loop recovery (same class as #56726, credit @Sahil-SS9 in PR #57414): a Windows renderer crash loop bearing the breakpoint exit code gets the same one-shot --no-sandbox relaunch instead of a dead window; unrelated crash loops keep the sandbox. - Manual --no-sandbox launches are honored but never made sticky. Tests: 15/15 windows-sandbox-fallback vitest; full desktop electron suite 432 passed / 1 skipped.
#56726) Surgical reapply of the surviving halves of PR #57414 by @Sahil-SS9 (the branch predates the ts-ify migration and the zoom apply/notify funnel, so a direct cherry-pick no longer applies): - Zoom persists to a main-process zoom-state.json as the primary store. The old localStorage-only store lives under Electron's cache/storage folders, which crash recovery can move or recreate — wiping zoom exactly when the user recovers from a crash. localStorage stays as a secondary mirror; pre-JSON installs migrate on first read. - Window geometry persists at ready-to-show, so a crash before the first resize/move/close still captures the restored bounds. The third half of #57414 (one-shot --no-sandbox relaunch on Windows renderer crash loops) was superseded by #66842, which ships the same recovery gated on the 0x80000003 sandbox-crash signature. Adapted to current main: restore/persist route through the applyZoomLevel funnel (39230d1) so the settings UI Scale control stays in sync, and JSON writes go through writeFileAtomic.
|
Merged via PR #66901 (#66901) — your zoom-persistence and window-state-on-show fixes are on main with your authorship preserved via rebase-merge. Thanks for the solid root-cause analysis on the localStorage/cache-folder interaction! Your branch predated the desktop ts-ify migration and the zoom apply/notify funnel, so the changes were surgically reapplied onto current main under your authorship rather than cherry-picked: restore/persist now route through The third part of this PR — the one-shot Closing this PR in favor of the merged salvage. Thanks again! |
…sandbox loss Follow-up to the salvaged NousResearch#66803 (@HexLab98): - Two-strike boot marker: a single mid-boot abort (task-manager kill, power loss) no longer disables the sandbox — only a second consecutive abort, or a signature-confirmed GPU/renderer STATUS_BREAKPOINT death, engages --no-sandbox. - Version-scoped stickiness: the fallback marker records the app version and re-probes the sandbox once after an update (new Electron or installer ACL repair may have fixed the host) instead of degrading forever. A failed re-probe returns straight to fallback. - Launch-time icacls repair now runs only when the marker shows a prior aborted boot (icacls /T recurses the whole install tree — healthy launches skip it; the installer grants the ACE at install time), and targets the install dir only. The userData grant is dropped: granting S-1-15-2-2 RX on userData would expose Hermes sessions/config to every AppContainer app on the machine. - Renderer crash-loop recovery (same class as NousResearch#56726, credit @Sahil-SS9 in PR NousResearch#57414): a Windows renderer crash loop bearing the breakpoint exit code gets the same one-shot --no-sandbox relaunch instead of a dead window; unrelated crash loops keep the sandbox. - Manual --no-sandbox launches are honored but never made sticky. Tests: 15/15 windows-sandbox-fallback vitest; full desktop electron suite 432 passed / 1 skipped.
NousResearch#56726) Surgical reapply of the surviving halves of PR NousResearch#57414 by @Sahil-SS9 (the branch predates the ts-ify migration and the zoom apply/notify funnel, so a direct cherry-pick no longer applies): - Zoom persists to a main-process zoom-state.json as the primary store. The old localStorage-only store lives under Electron's cache/storage folders, which crash recovery can move or recreate — wiping zoom exactly when the user recovers from a crash. localStorage stays as a secondary mirror; pre-JSON installs migrate on first read. - Window geometry persists at ready-to-show, so a crash before the first resize/move/close still captures the restored bounds. The third half of NousResearch#57414 (one-shot --no-sandbox relaunch on Windows renderer crash loops) was superseded by NousResearch#66842, which ships the same recovery gated on the 0x80000003 sandbox-crash signature. Adapted to current main: restore/persist route through the applyZoomLevel funnel (e90478b) so the settings UI Scale control stays in sync, and JSON writes go through writeFileAtomic.
Fixes #56726
Description
The Hermes desktop app on Windows was failing to open correctly due to three issues reported in #56726:
Renderer sandbox crash loop → dead black screen. On certain Windows setups, the Chromium/Electron renderer sandbox crashes deterministically on startup. The existing crash-loop suppression (3 crashes in 60s → stop reloading) left the user with a dead window and no recovery path. This PR adds a one-shot auto-relaunch with
--no-sandboxwhen a crash loop is detected on Windows, so the user gets a working window instead of an unusable app. The relaunch is gated by a one-shot flag to prevent infinite loops if--no-sandboxdoesn't help.Zoom level resets after crash recovery. Zoom was persisted only in the renderer's localStorage, which lives under Electron/Chromium cache storage. During crash recovery, these folders may be moved or recreated, wiping the zoom value. This PR adds a main-process JSON file (
zoom-state.json) as the primary zoom store, with localStorage kept as a secondary mirror for backward compatibility.restorePersistedZoomLevelnow prefers the JSON file and falls back to localStorage.Window geometry lost if app crashes before first resize/move/close. Window state was only persisted on resize, move, maximize, unmaximize, and close events. If the app crashed before any of those fired,
window-state.jsonwas never written. This PR adds aschedulePersistWindowState()call in theready-to-showhandler so the restored bounds are captured as soon as the window is visible.Verification
node -c apps/desktop/electron/main.cjs— syntax check passesnode --test apps/desktop/electron/window-state.test.cjs— 15/15 passnode --test apps/desktop/electron/bootstrap-platform.test.cjs— 10/10 passnode --test apps/desktop/electron/hardening.test.cjs apps/desktop/electron/update-relaunch.test.cjs apps/desktop/electron/session-windows.test.cjs— 42/42 passmain.cjschanged)