Skip to content

fix(desktop): close reconnect gaps that previously required CMD+R - #4737

Merged
wesbillman merged 4 commits into
mainfrom
brain/reconnect-cmdr-gaps
Aug 4, 2026
Merged

fix(desktop): close reconnect gaps that previously required CMD+R#4737
wesbillman merged 4 commits into
mainfrom
brain/reconnect-cmdr-gaps

Conversation

@wesbillman

Copy link
Copy Markdown
Collaborator

Opened by Brain (agent) on behalf of @wesbillman.

Problem

Users report the desktop app doesn't reliably reconnect and can wedge in states where only CMD+R (or a full restart) restores connectivity (thread c2205e2b in #desktop-reconnecting).

Pinky's empirical light-switch matrix (real buzz-relay, SIGTERM/1012 + SIGKILL × 1s/45s/3min, at f18a9cb10) passed 4/4 — the backoff state machine recovers cleanly from ordinary relay loss. That isolates the user-stuck states to four special cases a reload resets but the auto flow never did.

Fixes

Gap Change
G1 — recovery rode solely on the backoff timer (max 30s), throttled by WKWebView in occluded/background windows; nothing fired on network return or wake New useRelayResumeTriggers: online, window focus, and visibility→visible call preconnect() when the session is reconnecting/stalled, rate-limited to one attempt per 5s (relayResumeTriggerPolicy.ts). Deliberately inert for the terminal disconnected state.
G2 — any AUTH OK false latched the session terminal forever, though the relay also rejects for transient causes (duplicate-AUTH "already authenticated" race, ±60s clock skew, fail-closed allowlist DB errors) New AuthOkTracker (relayAuthPolicy.ts): "already authenticated" resolves as success; transient rejections retry with normal backoff; latch only on restricted: or after 3 consecutive rejections.
G3 — an auth-required: CLOSED (REQ racing AUTH after reconnect) permanently deleted the live subscription with no UI signal — frozen channel while state reads "connected" Reclassified auth-required: as retryable in relayClosedPolicy.ts. Genuinely terminal classes (restricted:, invalid:, …) still delete. Can't loop: a truly unauthenticated session latches terminal at the connection level.
G4useRelayAutoHeal observed the 2s-debounced connection hook, so sub-2s flaps never triggered the heal even though resetConnection had already rejected every in-flight query Auto-heal now observes the raw connection-state emitter. The existing 15s heal rate-limit still guards against flap storms.

Each fix is a colocated pure-policy module + unit tests, matching the existing relayReconnectPolicy/relayClosedPolicy pattern.

Validation

  • Full desktop unit suite: 4151 pass, 0 fail (at branch tip, pnpm -C desktop test)
  • pnpm -C desktop typecheck and pnpm -C desktop check clean (file-size ratchet respected — relayClientSession.ts net −2 lines despite the tracker wiring)
  • Evidence trail: RESEARCH/DESKTOP_RECONNECT_CMDR_GAP_AUDIT.md (audit), RESEARCH/DESKTOP_RECONNECT_LIGHT_SWITCH_RESULTS.md (Pinky's matrix)

Not covered / follow-ups

  • Native macOS sleep-wake was not automated (would kill the harness session); G1's focus trigger is the mechanism that covers wake in practice, but a manual sleep-wake verification on a real build is worthwhile.
  • G3 terminal-CLOSED classes (restricted: etc.) still silently delete subs with no UI signal — surfacing that is a separate UX decision.
  • Stall-watchdog latency (60s idle + 10s check) left unchanged; G1 triggers largely mask it.

Users report the desktop app can wedge in states where only a manual
CMD+R (or full restart) restores connectivity. Pinky's light-switch
matrix (real relay, SIGTERM/SIGKILL x 1s/45s/3min) proved the backoff
state machine itself recovers cleanly, isolating the failures to four
special states a reload resets but the auto flow never did:

- G1 resume triggers: recovery rode solely on the backoff timer
  (max 30s), which WKWebView throttles in occluded/background windows —
  after sleep/VPN reauth the scheduled attempt may not fire until focus.
  New useRelayResumeTriggers fires preconnect() on network online,
  window focus, and visibility-visible when the session is
  reconnecting/stalled, rate-limited to one attempt per 5s.
  Deliberately inert for the terminal 'disconnected' state.

- G2 terminal AUTH latch: any AUTH OK=false latched the session
  terminal forever, but the relay also rejects for transient causes
  (duplicate-AUTH 'already authenticated' race, +/-60s clock skew,
  fail-closed allowlist DB errors). New AuthOkTracker treats
  'already authenticated' as success, retries transient rejections
  with normal backoff, and only latches on 'restricted:' or after 3
  consecutive rejections.

- G3 terminal CLOSED: an 'auth-required:' CLOSED (REQ racing the AUTH
  handshake after reconnect) permanently deleted the live subscription
  with no UI signal — frozen channel while the state reads connected.
  Reclassified as retryable; genuinely terminal classes still delete.

- G4 sub-2s flap heal: useRelayAutoHeal observed the 2s-debounced
  connection hook, so a brief flap never registered a degraded->
  connected transition even though resetConnection had already
  rejected every in-flight query. It now observes the raw
  connection-state emitter; the existing 15s heal rate-limit still
  guards against flap storms.

Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
@wesbillman
wesbillman requested a review from a team as a code owner August 4, 2026 15:37
Co-authored-by: npub1gjuws2a2dc8z2nszprtg7v6u9q7ffeah3hgl5yx45jwn7y7aqs6s5e9xj6 <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@buzz.block.builderlab.xyz>
Signed-off-by: npub1gjuws2a2dc8z2nszprtg7v6u9q7ffeah3hgl5yx45jwn7y7aqs6s5e9xj6 <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@buzz.block.builderlab.xyz>

@wpfleger96 wpfleger96 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The reconnect coverage is strong, but two AUTH policy interactions can keep permanent failures retrying instead of reaching the intended terminal state.

Comment thread desktop/src/shared/api/relayClientSession.ts
Comment thread desktop/src/shared/api/relayAuthPolicy.ts Outdated
npub1yxv5wk0u0fh6dwt925wntn7h397jvteyj4r87ttcd9xae7n2t3lqqj9jmm and others added 2 commits August 4, 2026 11:25
Review findings from wpfleger96 on PR #4737, both confirmed:

1. Resume triggers (focus/online/visibility) called preconnect(), which
   resets the AUTH rejection streak — a resume event interleaved with
   repeated AUTH rejections could clear the counter before the next
   OK=false, making MAX_CONSECUTIVE_AUTH_REJECTIONS unreachable and
   retrying a dead session forever. Resume now uses a separate
   resumeReconnect() path that bypasses the pending backoff timer but
   preserves the terminal latch and rejection streak; only explicit
   re-engagement (reconnect card / community switch) resets them.

2. The relay's permanent ban response is 'blocked: you are banned from
   this community' (handlers/auth.rs ban seam), which classified as
   retry. 'blocked:' now latches terminal immediately; the policy test
   covers the relay's exact ban string.

Also fixes a latent race the new integration test exposed: a late
failure from an in-flight publish/subscribe retry landing after the
terminal latch called resetConnection() with default options and
painted 'reconnecting' over the terminal 'disconnected' state —
permanently, since the latch blocks actual reconnection.
resetConnection now preserves 'disconnected' when the session is
terminal.

New e2e regression: three AUTH rejections interleaved with resume
events still latch terminal on the third, and later resume events do
not revive the session.

Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
…gaps

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
@wesbillman
wesbillman force-pushed the brain/reconnect-cmdr-gaps branch from c3cf3d5 to 81c964d Compare August 4, 2026 17:49

@wpfleger96 wpfleger96 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Both prior findings are addressed at this head: environment resume now bypasses backoff without resetting AUTH/terminal policy state, and blocked: ban responses latch terminal immediately. The interleaved resume/AUTH rejection regression is covered end to end. No remaining blocking findings.

@wesbillman
wesbillman enabled auto-merge (squash) August 4, 2026 18:04
@wesbillman
wesbillman merged commit e5efd04 into main Aug 4, 2026
46 of 48 checks passed
@wesbillman
wesbillman deleted the brain/reconnect-cmdr-gaps branch August 4, 2026 18:33
loganj pushed a commit that referenced this pull request Aug 4, 2026
…links

* origin/main:
  fix(desktop): close reconnect gaps that previously required CMD+R (#4737)

Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
wpfleger96 pushed a commit that referenced this pull request Aug 4, 2026
…-enabled

* origin/main:
  fix(desktop): close reconnect gaps that previously required CMD+R (#4737)

Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
wpfleger96 added a commit that referenced this pull request Aug 4, 2026
* commit 'ce3cf3cd2': (76 commits)
  Polish Huddle voice controls (#4694)
  fix(local-archive): default both archive settings to enabled (#4750)
  fix(mobile): stop oversized read-state retry loop (#4595)
  fix(desktop): close reconnect gaps that previously required CMD+R (#4737)
  Dock Buzz Term within channel workspace (#4724)
  perf(relay): index channel-id lookups and skip trace-only reads (#4647)
  fix(agents): canonicalize stale persona harness pins (#4631)
  Refine community invite links (#4734)
  feat(desktop): persist sidebar observed-unread across webview reload (#3976)
  feat(desktop): surface config diff in restart-required badge (#3637)
  Polish sidebar unread hierarchy (#4573)
  fix(desktop): show cached display names on startup (#3317)
  docs(acp): explain per-channel session model in base prompt (#4729)
  docs(nip-am): normative amendment — cache SHOULD/MUST + pricingIdentity + consumer cost guidance (#4632)
  Remove blur from Welcome composer guidance (#4691)
  Refine desktop timeline activity presentation (#4582)
  Defer desktop media uploads until send (#4522)
  fix(desktop): stop clipping focus ring on channel intro action cards (#2392) (#4374)
  Polish mobile inbox and media flows (#4512)
  feat: ship Buzz Term (#4347)
  ...

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
shellz-n-stuff added a commit to shellz-n-stuff/buzz that referenced this pull request Aug 4, 2026
…gent-instructions

* origin/main: (30 commits)
  feat: Buzz entity links — rich preview cards + in-app navigation for repos, PRs, and issues (block#4695)
  fix(desktop): serialize tray channel actions for frontend (block#4762)
  chore(release): release Buzz Desktop version 0.5.5 (block#4788)
  feat(projects): support multiple repositories (block#4671)
  fix(ci): make desktop cache test version agnostic (block#4791)
  fix(desktop): widen post-Enter timeouts in empty-edit-delete spec (block#4792)
  fix(desktop): wait for terminal frame before splash (block#4781)
  fix(desktop): integer-align custom reaction emoji (block#4779)
  Polish Huddle voice controls (block#4694)
  fix(local-archive): default both archive settings to enabled (block#4750)
  fix(mobile): stop oversized read-state retry loop (block#4595)
  fix(desktop): close reconnect gaps that previously required CMD+R (block#4737)
  Dock Buzz Term within channel workspace (block#4724)
  perf(relay): index channel-id lookups and skip trace-only reads (block#4647)
  fix(agents): canonicalize stale persona harness pins (block#4631)
  Refine community invite links (block#4734)
  feat(desktop): persist sidebar observed-unread across webview reload (block#3976)
  feat(desktop): surface config diff in restart-required badge (block#3637)
  Polish sidebar unread hierarchy (block#4573)
  fix(desktop): show cached display names on startup (block#3317)
  ...

Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
sandro-sq added a commit that referenced this pull request Aug 4, 2026
* origin/main: (31 commits)
  feat: paste composer text without formatting (#4801)
  Revert "chore(release): release Buzz Desktop version 0.5.5" (#4808)
  chore(release): release Buzz Desktop version 0.5.5 (#4800)
  fix: reauthenticate databricks model discovery (#4008)
  Revert "chore(release): release Buzz Desktop version 0.5.5" (#4797)
  feat: Buzz entity links — rich preview cards + in-app navigation for repos, PRs, and issues (#4695)
  fix(desktop): serialize tray channel actions for frontend (#4762)
  chore(release): release Buzz Desktop version 0.5.5 (#4788)
  feat(projects): support multiple repositories (#4671)
  fix(ci): make desktop cache test version agnostic (#4791)
  fix(desktop): widen post-Enter timeouts in empty-edit-delete spec (#4792)
  fix(desktop): wait for terminal frame before splash (#4781)
  fix(desktop): integer-align custom reaction emoji (#4779)
  Polish Huddle voice controls (#4694)
  fix(local-archive): default both archive settings to enabled (#4750)
  fix(mobile): stop oversized read-state retry loop (#4595)
  fix(desktop): close reconnect gaps that previously required CMD+R (#4737)
  Dock Buzz Term within channel workspace (#4724)
  perf(relay): index channel-id lookups and skip trace-only reads (#4647)
  fix(agents): canonicalize stale persona harness pins (#4631)
  ...

Signed-off-by: Alessandro Joabar <sandro@squareup.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants