Files
buzz/desktop/src-tauri
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger f42a301af1 fix(relay-reconnect): resilient reconnect with fast-path, escalation, and polling
The reconnect button had three compounding failure modes:

1. Instant false-fail during sign-in: the transport-recovery hook ran and
   exited in ~0.1s, but the tunnel liveness check passed while the Access
   layer was still blocked, so preconnect() hit the unreachable network and
   failed immediately — the user saw a 'failed' toast mid-login.

2. False-fail after actual reconnect: a single promise race with a 15s cap
   determined the verdict. preconnect() set keepAliveRequested=true so the
   background retry loop kept going and often succeeded right after the
   toast fired, leaving the user confused.

3. Spurious browser tab on transient blips: the hook ran unconditionally
   even when the transport was healthy and the disconnect was a network flap.

New strategy — try-fast-first → escalate → poll-until-connected:

- Phase 1 (fast path): attempt preconnect() with a 4s cap. Succeeds
  immediately for transient blips — no hook fires, no browser tab opens.

- Phase 2 (escalation, hook-configured builds only): only if the fast path
  fails and relay_reconnect_hook_configured() returns true, run the
  transport-recovery hook. The hook returns quickly; any browser-based
  network sign-in it triggers runs asynchronously.

- Phase 3 (poll-until-connected): retry preconnect() every 3s and watch the
  connection-state emitter. The moment the relay is reachable the next poll
  or emitter event declares success — no waiting for a timer. A 120s backstop
  fires a soft toast only; keepAliveRequested keeps the background loop alive.

Extracts a RelayReconnectController singleton (plain TS, injected deps) so
all three consumers — ConnectionBanner, useSidebarRelayConnectionCard, and
ProfileStep — share one in-flight state via useSyncExternalStore-style
subscribe. No double browser tabs; consistent 'Waiting for sign-in' state
across surfaces. Cancellation token (generation counter) is checked after
every await and inside every async continuation, closing the gap where a
cancelled poll's .then() could still mutate state.

Adds relay_reconnect_hook_configured Rust command (OSS-clean, bool) so the
frontend skips escalation and 'Waiting for sign-in...' copy in OSS builds.
e2eBridge returns false for this command in test environments.

Tests rewritten to exercise the real RelayReconnectController class with
injected fakes — no simulation helpers.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-02 11:26:49 -04:00
..
2026-07-01 10:58:43 -07:00