Skip to content

perf(update): stop the app looking closed during update and restart - #5358

Merged
Pulkit7070 merged 9 commits into
mainfrom
perf/update-restart-visibility
Sep 14, 2026
Merged

Pulkit7070 merged 9 commits into
mainfrom
perf/update-restart-visibility

Conversation

@Pulkit7070

Copy link
Copy Markdown
Collaborator

Splits the update-latency and boot-robustness work out of #5355. The sidebar restart-order fix that #5355 also carried is dropped in favor of #5348, which fixes that root cause more completely.

Problem

Updating the desktop app left it looking closed for up to a minute. There is no single 60s timeout: the gap is stacked waits, and the largest piece (the OS-level bundle/installer swap) is native to the updater and not reducible by us. The reducible parts are that the app showed nothing during that window and the new backend was slow to come back.

Changes

  • Instant paint: a static splash inside index.html removes the blank frame before React mounts, on every OS and on both cold and post-update launch.
  • "Updating / Restarting AO" copy: a cross-platform post-update marker (update-relaunch-flag) lets the loader tell the user an update is in progress instead of a generic "connecting" (previously only macOS knew). New i18n strings across all locales.
  • macOS helper: presents its window on the normal close/install path so the swap window is not blank.
  • Backend boot trimmed: agent-readiness warming (~24 subprocess probes) is moved off the synchronous boot critical path into the post-ready callback, reducing contention with the port-gating work. Safety fences are untouched and still run before the port binds.
  • Boot robustness: terminal handoff artifact cleanup on boot is made non-fatal (logs a warning instead of failing startup); it is best-effort maintenance, not a safety invariant.

Verification

  • Backend: build/vet clean; session_manager and daemon suites pass. New test covers the non-fatal cleanup.
  • Frontend: typecheck passes.
  • Update timing across OSes is validated by reading the code paths and simulating the post-update marker, not by a live packaged update per OS (that needs a packaged build and a real release channel, which cannot be exercised from a dev checkout).

Out of scope

Keeping the daemon alive across the update (removes the backend re-boot but touches supervisor lifecycle and SQLite single-owner guarantees) and reducing the native OS installer swap (not reducible by us).

Deleting a terminal agent-switch's leftover private artifacts is best-effort
maintenance, not a safety invariant. A file left behind can be undeletable
across restarts (e.g. a still-open handle or read-only attribute on Windows),
so folding it into the boot-fatal error would refuse to bind the daemon
forever. Record it as a maintenance fault and continue.
Reduce and mask update-restart latency without touching daemon safety fences:
- Instant pre-React splash in index.html so there is no blank frame on boot.
- Cross-platform post-update marker so the startup loader shows Updating/
  Restarting copy instead of Connecting after an update relaunch.
- Present the macOS update helper window on the normal swap path, not only on
  stall/failure.
- Relocate agent-readiness warming to the post-ready callback so its ~24
  subprocess probes no longer contend with the synchronous work that gates the
  port bind.
Address review of the update-restart visibility work:
- The pre-React splash hardcoded the dark native background, flashing dark
  before the (default light) app theme resolved. Follow prefers-color-scheme
  so it matches the native window background in both themes.
- consumeUpdateRelaunchFlag threw on a marker that parsed to a non-object
  (e.g. literal null); it now reads as "not an update" instead, honoring the
  never-block-startup contract. Drop the fromPID==pid guard so PID reuse after
  relaunch no longer produces a false negative; version + age + one-shot
  consume already gate correctness.
- Time-bound the marker write before quitAndInstall so a hung filesystem
  write cannot stall the install.
- Add regression tests for the marker contract.
The e2e tsconfig typecheck (renderer-smoke and test jobs) failed because
the fake bridge did not implement the new updates.isPostUpdateRelaunch
method. Add it to both update mocks.
The post-update relaunch check read aoBridge.updates.isPostUpdateRelaunch
directly, which threw "Cannot read properties of undefined" in renderer
tests and any preload/web fallback where the updates namespace is not
present. Guard the access with optional chaining and a typeof check so a
missing bridge simply means "not a post-update relaunch" and the loader
still mounts.
quitAndInstallUpdate now writes the post-update relaunch marker (bounded)
before calling quitAndInstall, so the non-darwin install defers past a
microtask. The translocation-path test called it without awaiting and
asserted quitAndInstall synchronously, which no longer holds. Await the
call so the assertion runs after the marker write completes.
@i-trytoohard i-trytoohard added bug Something isn't working comp/desktop Electron main process and React renderer. labels Sep 14, 2026
@i-trytoohard i-trytoohard added this to the Release & platform milestone Sep 14, 2026
@illegalcall
illegalcall self-requested a review September 14, 2026 10:49
@Pulkit7070
Pulkit7070 merged commit 0646772 into main Sep 14, 2026
14 checks passed
ronishrohan pushed a commit that referenced this pull request Sep 14, 2026
The static splash in index.html duplicates the existing React startup loader and briefly shows a hand-drawn SVG before the real app mounts. Restore the empty React root so startup follows the pre-#5358 behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working comp/desktop Electron main process and React renderer.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants