Electron derived userData from package.json's `@backspace/desktop` name, leaking
the monorepo's pnpm scope into ~/Library/Application Support/. Now `app.setName`
runs at module load before any userData consumer, and a one-shot migration
atomically moves the historical folder to <appData>/Backspace, cleaning the
empty @backspace/ parent. Conservative on conflict — never clobbers an existing
populated target. EXDEV fallback to recursive copy. Smoke-recovery path flipped
back to Backspace.
Three issues found while running the script for the first time:
1. userData path was hardcoded to ~/Library/Application Support/Backspace.
Electron actually uses ~/Library/Application Support/@backspace/desktop
(derived from package.json#name when app.name is not set explicitly).
Scenario 2's bad URL was being written to the wrong location, so the app
never loaded it and recovery never entered. Path corrected.
2. Scenario 4's VITE_FORCE_BOOT_STALL approach didn't actually stall anything
in practice: the desktop loads the user's REMOTE instance URL, which
serves its own web bundle from the server. The locally rebuilt stalled
bundle is never used at runtime. Replaced with a tiny static HTML page
served via `python3 -m http.server` and pointed at via BACKSPACE_URL.
The page has no window.backspace integration, so it never pings,
guaranteeing the boot timer fires. No web/desktop rebuilds needed.
3. Scenario 13 (positive control: rendererReady ping disarms the timer)
used assert_no_log which was never defined. The function is now defined
alongside wait_for_log. Scenario 13 now correctly verifies that a page
that DOES call rendererReady never triggers recovery.
All three automated scenarios now pass:
✓ Scenario 2: load-failed recovery entered
✓ Scenario 4: renderer-stalled recovery entered
✓ Scenario 13: boot timer correctly disarmed by rendererReady ping
- App.tsx and main.tsx ErrorBoundary gate rendererReady() on
VITE_FORCE_BOOT_STALL build env var. When set, the ping is suppressed
so the main-process boot timer fires (exercising the renderer-stalled
recovery path without hand-editing source).
- vite-env.d.ts declares the env var type so TS doesn't complain.
- scripts/smoke-recovery.sh automates scenarios 2 (bad URL → load-failed)
and 4 (forced stall → renderer-stalled), grepping stderr for the
[recovery] entered: ... lines added by the prior commit. Backs up and
restores the user's instance-url.json. Manual scenarios documented in
the script header.