fix(desktop): bots wake on the right gateway — session RPCs route to the owning profile (#89206) - #89475
Merged
Conversation
…#89206) Bot Mode wake-ups died on a routing split-brain: session.resume / session.activate / session.usage were dispatched on whatever socket was active at request time, while the bot's own backend sat healthy and idle (zero traffic until the idle reaper killed it). Two divergence sources, both fixed: 1. Registry-owned route truth. applyActive() now publishes the active route's bare profile ($activeGatewayRoute + onActiveRouteChanged), and use-gateway-boot mirrors it into $activeGatewayProfile. Previously, eviction fallbacks (idle reap, connection removal, profile delete) moved the SOCKET back to the primary while the profile atom kept naming the evicted bot — ensureGatewayProfile's "already active" fast path then trusted the stale atom and skipped the re-swap forever. 2. Request-time routing for session-scoped RPCs. resumeSession's RPCs go through requestForSessionProfile (store/session-request-router.ts): when the active route serves the session's owning profile the ambient dispatcher is kept (reauth-aware reconnect); when it diverges — a concurrent switch won the mutex, a failed dial left the old socket active, an eviction re-pointed the route — the RPC is pinned to the owning profile's own socket via requestGatewayForProfile. Diagnosed from zero trust's debug bundle (loki/hulk/teknium-kun backends READY then idle-reaped, renderer stuck on "Waking up… → retries gave up") and DanBennettUK's #89206 trace (profile socket accepts, closes with messages=0, no resume RPC observed). Both layers sabotage-proven: reverting the route publish fails the lockstep/eviction tests; reverting the request-time routing fails the wrong-socket dispatch test.
૮ >ﻌ< ა ci reviewran on f1bf53b — fix(desktop): route session RPCs to the profile that owns th
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bot Mode bots wake reliably: session-scoped RPCs (
session.resume/session.activate/session.usage) now always reach the backend that owns the session's profile, instead of whatever socket happened to be active at dispatch time.Root cause (diagnosed from zero trust's debug bundle + @DanBennettUK's #89206 trace): a routing split-brain. The renderer's
$activeGatewayProfileatom and the gateway registry's actual socket selection could diverge — eviction fallbacks (idle reap, connection removal, profile delete) moved the socket back to the primary while the atom kept naming the evicted bot, soensureGatewayProfile's "already active" fast path skipped the re-swap forever and every resume went out on the default backend. Evidence: loki/hulk/teknium-kun backends booted, passed HTTP+WS probes, then sat at zero traffic until the idle reaper killed them, while the renderer burned its bounded retries into "automatic retries gave up".Changes
store/gateway.ts—applyActive()publishes the active route's bare profile ($activeGatewayRoute+ newonActiveRouteChangedregistry callback), in the same synchronous step that selects the socket. Every eviction/fallback path already funnels throughapplyActive, so the published profile can never linger on a deselected backend.app/gateway/hooks/use-gateway-boot.ts— wiresonActiveRouteChangedto mirror the registry's route into$activeGatewayProfile(registry leads; renderer follows).store/session-request-router.ts(new) —requestForSessionProfile(): session-scoped RPCs re-resolve their route at request time; when the active gateway serves the owning profile the ambient dispatcher is kept (reauth-aware reconnect), otherwise the RPC is pinned to the profile's own socket viarequestGatewayForProfile.app/session/hooks/use-session-actions/index.ts—resumeSession's three session-scoped RPCs go through the router.store/session-request-router.test.ts(new) — 7 tests covering route-publish lockstep, eviction fallback, and wrong-socket dispatch.Validation
npm run check:lint(3 tsconfigs + eslint)Fixes the remaining half of #89206 (the hydration-gate half landed in #89394).
Infographic