Skip to content

fix(desktop): boot retry, post-reconnect busy reconciliation, refresh coalescing - #58109

Open
rod-nxtlevel wants to merge 1 commit into
NousResearch:mainfrom
rod-nxtlevel:fix/desktop-boot-retry-reconcile-coalesce
Open

fix(desktop): boot retry, post-reconnect busy reconciliation, refresh coalescing#58109
rod-nxtlevel wants to merge 1 commit into
NousResearch:mainfrom
rod-nxtlevel:fix/desktop-boot-retry-reconcile-coalesce

Conversation

@rod-nxtlevel

Copy link
Copy Markdown
Contributor

Problem

Three renderer lifecycle gaps, reproduced on a Desktop running in global remote mode with many concurrent sessions:

  1. Dead-at-launch is unrecoverable without a click: reconnectNow() early-returns while !bootCompleted, and a failed boot() only latches the failure — so a backend unreachable at app start (VPN tunnel not yet up after login) leaves the boot-failure overlay until a manual retry, even after the network comes back.
  2. Mid-stream disconnect strands the composer on 'Thinking…' forever: on reconnect only refreshHermesConfig()+refreshSessions() run. Nothing resets per-session busy/awaitingResponse for turns whose message.complete died with the socket; the 8-min watchdog clears only sidebar spinners; the running:false guard deliberately skips pre-first-delta turns.
  3. Event-driven refresh storms: every session.info triggers an unthrottled /api/config + /api/config/defaults double-fetch, and every message.complete from any profile socket triggers a 4-request sidebar fan-out (recents + cron sessions + cron jobs + messaging). With N busy sessions this is O(N) request storms at the backend.

Plus the renderer twin of the main-process misclassification fixed in #58108: resolveGatewayWsUrl wraps any ticket-mint failure into GatewayReauthRequiredError.

Fix

  • resolveGatewayWsUrl (apps/shared/websocket-url.ts) wraps mint failures as reauth only when auth-shaped (new isGatewayAuthShapedError: needsOauthLogin / statusCode 401-403 / word-bounded 401|403 markers / canonical reauth phrasings — IPC-flatten-safe); transport errors rethrow unchanged so every caller keeps its backoff.
  • Failed initial boot auto-retries on 5s/15s/30s/60s-cap backoff with ±20% jitter, and wake/online/visibility nudges re-drive boot() immediately when the previous failure was transport-shaped. Genuine auth failures latch (sign-in overlay stays; no thrash). Double-boot guarded; retry timer cleaned on unmount.
  • New useReconnectReconciliation: after a successful reconnect (primary or secondary socket), queries session.active_list on that gateway and clears busy/awaitingResponse/streamId for cached-busy sessions that are no longer working/starting/waiting — composer included. Probe failure keeps every latch (conservative). Secondary sockets get the same treatment via a new optional onReconnected registry hook.
  • New createTrailingCoalescer (trailing coalesce + single-flight + at-most-one queued follow-up): refreshSessions coalesced at 1.5s with the cron/messaging ancillary fan-out throttled to once per 30s; session.info config refetch debounced at 5s and gated to events that can actually change config-derived state.

Tests

31 new/updated vitest cases across gateway-ws-url.test.ts (20), use-gateway-boot.test.tsx (transport-retry/auth-latch/nudge/reconcile-once), use-reconnect-reconciliation.test.tsx (9), refresh-coalescer.test.ts (5), use-session-list-actions.test.tsx (3), config-refresh.test.tsx (4). Full desktop typecheck + lint clean; no regressions in the suite (verified against a stash baseline).

🤖 Generated with Claude Code

- resolveGatewayWsUrl maps mint failures to reauth only when auth-shaped
  (401/403/needsOauthLogin); transport errors stay retryable.
- Failed initial boot retries on 5s/15s/30s/60s jittered backoff and on
  wake/online/visibility nudges; genuine auth failures latch with the
  sign-in overlay instead of thrashing.
- After a socket reconnect (primary or secondary), reconcile cached busy
  sessions against session.active_list — clears composers stuck on
  'Thinking…' when message.complete died with the socket.
- Coalesce refreshSessions (1.5s trailing + single-flight, ancillary
  cron/messaging fan-out throttled to 30s) and debounce session.info
  config refetches (5s, gated to config-bearing events).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alt-glitch alt-glitch added type/bug Something isn't working comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have labels Jul 4, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: Approved (read-only token - formal approval deferred)

Small, well-scoped fix. No concerns.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Approved (LGTM)

Desktop boot retry, post-reconnect busy reconciliation, and refresh coalescing. Multiple UX improvements for the desktop app. Well-scoped.

What Looks Good

  • Multiple related desktop improvements
  • No security concerns
  • No debug artifacts

Reviewed by Hermes Agent

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Comment (large surface area)

Desktop boot retry PR (+1139/-28, 17 files): boot retry, post-reconnect busy reconciliation, refresh coalescing. Substantial but focused on the desktop restart/reconnect lifecycle.

Looks Good

  • Well-scoped by concern (desktop lifecycle)
  • Error handling and retry logic appear sound

Reviewed by Hermes Agent

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the verified boot/reconnect and refresh-storm paths. The current main still has the initial-boot guard at apps/desktop/src/app/gateway/hooks/use-gateway-boot.ts:210 and the unconditional session.info config refresh at apps/desktop/src/app/session/hooks/use-message-stream/gateway-event.ts:277.

Problems

  • apps/desktop/src/app/session/hooks/use-message-stream/index.ts:45 makes scheduleSessionsRefresh required, but two current-main harnesses added after this PR's July 3 base still pass refreshSessions: compaction-event.test.tsx:27 and approval-mode-event.test.tsx:27. Update both to the new callback shape before salvaging; apps/desktop/tsconfig.json includes src, and package.json typecheck runs tsc -p . --noEmit.

Suggested changes

  • Pass scheduleSessionsRefresh: vi.fn() in those two harnesses and run the desktop typecheck plus affected Vitest files after reconciling with current main.

This is an automated hermes-sweeper review.

/** Coalesced sidebar refresh — every message.complete asks for one, so the
* callback must debounce/single-flight internally (scheduleSessionsRefresh),
* not fire a full 4-fetch fan-out per completed turn. */
scheduleSessionsRefresh: () => void

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This required rename also needs the current-main compaction-event.test.tsx and approval-mode-event.test.tsx harnesses updated: both still pass refreshSessions only. They were added after this PR's base, so include them in the salvage to keep tsc -p . --noEmit green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants