Files
orca/src/shared/renderer-shutdown-events.ts
T
Brennan Benson 290f192d84 fix(updater): surface and degrade renderer shutdown checkpoint failures (STA-5505) (#16497)
* fix(updater): surface and degrade renderer shutdown checkpoint failures

The in-app updater could refuse to install with 'Renderer shutdown
checkpoint was not completed.' while the actual persist() error was
swallowed unlogged, leaving users stranded on old builds (STA-5505).

- report the swallowed persist error: console, crash breadcrumb, and a
  cross-world DOM attribute so the thrown error (and the Update Error
  dialog) names the underlying cause
- stop failing the checkpoint on sleeping-agent quit-capture errors; the
  periodic capture bounds the loss to one minute
- extend the existing durable-session degradation to full-session staging
  failures during an intentional restart, preserving the dirty-draft guard

* fix(quit): degrade and surface checkpoint-vetoed app quits (#15352)

Cmd+Q walked the same shutdown checkpoint as the updater: a persist()
throw preventDefault()ed the synthetic beforeunload and
confirmNativeWindowClose returned silently — quit accepted, nothing
logged, SIGKILL the only exit.

- run the quit checkpoint inside a window-close scope so full-session
  staging failures degrade to the durable tier for app-level closes too
  (dirty editor drafts still hard-block)
- when the checkpoint still vetoes the quit, toast the published failure
  reason instead of dying silently

* fix(updater): retry-then-degrade staging and honest capture-loss accounting

Review findings on the first pass:
- a first full-session staging failure now stays a visible, retryable
  error; only a repeat failure degrades to durable-only staging, so a
  transient IPC failure keeps its retry instead of silently dropping
  just-captured scrollback
- the sleeping-capture comment no longer overstates periodic coverage
  (periodic mode skips done panes and never stamps quit origin); the
  swallowed failure records a crash breadcrumb
- pin the exact degradable-shutdown gate expression in the source-shape
  test so rewiring it cannot pass silently

* fix(updater): arm the staging-retry flag only for degradable shutdowns

An unrelated unload's staging failure must not burn the visible first
retry of a later restart or quit.

* fix(updater): isolate shutdown checkpoint retries

Reset full-session staging retry state when a shutdown attempt is abandoned, and route Terminal-less closes through the same scoped synthetic checkpoint as mounted workspaces. Keep arbitrary thrown-value diagnostics non-throwing and localize the quit failure toast.

* fix(updater): preserve checkpoint retry lifecycle

* fix(updater): preserve empty checkpoint failure reason
2026-08-25 21:14:20 -07:00

57 lines
2.0 KiB
TypeScript

export const ORCA_RENDERER_UNLOAD_PREVENTED_EVENT = 'orca:renderer-unload-prevented'
export const ORCA_RENDERER_SHUTDOWN_CHECKPOINT_FAILED_EVENT =
'orca:renderer-shutdown-checkpoint-failed'
export const ORCA_RENDERER_SHUTDOWN_CHECKPOINT_ABORTED_EVENT =
'orca:renderer-shutdown-checkpoint-aborted'
// Why a DOM attribute: the checkpoint guard runs in the renderer's main world while
// prepareRendererForAppRestart runs in the context-isolated preload world. Events
// cross worlds but their JS payloads don't; document attributes are shared platform
// state, so this is the one channel that carries the failure reason to the thrower.
export const ORCA_SHUTDOWN_CHECKPOINT_FAILURE_REASON_ATTRIBUTE =
'data-orca-shutdown-checkpoint-failure'
export function formatShutdownCheckpointFailureReason(error: unknown): string {
try {
const reason = String(error instanceof Error ? error.message : error)
return reason || 'Unknown shutdown checkpoint failure'
} catch {
return 'Unknown shutdown checkpoint failure'
}
}
export function publishShutdownCheckpointFailureReason(reason: string): void {
try {
globalThis.document?.documentElement?.setAttribute(
ORCA_SHUTDOWN_CHECKPOINT_FAILURE_REASON_ATTRIBUTE,
reason
)
} catch {
// Best-effort diagnostics; the checkpoint verdict itself is carried by the event.
}
}
export function clearShutdownCheckpointFailureReason(): void {
try {
globalThis.document?.documentElement?.removeAttribute(
ORCA_SHUTDOWN_CHECKPOINT_FAILURE_REASON_ATTRIBUTE
)
} catch {
// Best-effort diagnostics only.
}
}
/** Read and clear the published reason so a stale one can't label a later failure. */
export function consumeShutdownCheckpointFailureReason(): string | null {
try {
const root = globalThis.document?.documentElement
const reason = root?.getAttribute(ORCA_SHUTDOWN_CHECKPOINT_FAILURE_REASON_ATTRIBUTE)
if (reason) {
root?.removeAttribute(ORCA_SHUTDOWN_CHECKPOINT_FAILURE_REASON_ATTRIBUTE)
}
return reason || null
} catch {
return null
}
}