c4bddd9a4e
Branching a session owned by one connection while another is active created the child on the wrong backend — or nowhere — while the sidebar still painted an optimistic row. That row pointed at an id no backend owned, so hydration retried, exhausted, and armed the stranded-session overlay: "Couldn't load this session. The connection to this session failed and automatic retries gave up." Retry re-ran the same mis-route, so it never recovered. branchStoredSession and branchCurrentSession resolved only the parent's PROFILE and called ensureGatewayProfile(profile), then dispatched through the ambient requestGateway. A profile name does not identify a backend once several connections expose the same name, so both the parent transcript read and the session.create/session.branch RPC landed on whichever socket happened to be active. removeSession, twelve lines away, already routed by (connectionId, profile) via SessionOwnerScope — branch simply never got the same treatment. Reuse that existing contract: derive the exact owner from the parent row with sessionOwnerRouteFromRow, activate it with ensureGatewayAgent, and dispatch via requestGatewayForAgent. getAllSessionMessages takes the same owner scope so the transcript read cannot silently come back empty and abort the branch as "nothing to branch" before any create is attempted. Both arms of forkBranch (session.branch for the open chat, session.create for a sidebar right-click) are covered. An untagged parent row — the single-backend case — keeps the previous profile-only path exactly, so behaviour is unchanged for users with one connection. Tests: three call-site regressions asserting the create rides the owning (connection, profile) socket, that the transcript read carries the same owner scope, and that an untagged parent still uses the ambient socket. Plus an integration test that mocks nothing inside the router — the real requestGatewayForAgent runs against a fake Electron bridge and transport, so a regression that re-collapses a registry route onto the ambient socket fails even if the call-site assertions still pass.