fix(desktop): profile switches publish atomically without ever failing closed (#46651, #89622) - #89797
Conversation
… mid-dial entries (#89622, #46651) Re-lands the goals of the reverted atomic-publish series (#89483, reverted in #89785) without the fail-closed decline path that killed profile clicks, and fixes the underlying pruner race the original series exposed. Two changes relative to the restored (pre-series) behavior: - Publication is atomic and mode-safe (#46651): the switch resolves the target's connection descriptor CONCURRENTLY with the socket work and publishes $activeGatewayProfile + $connection in one nanostores batch() frame, so no request or plugin mode-listener observes the new gateway beside the previous profile's descriptor. Unlike the reverted series, a failed descriptor lookup fails OPEN — the switch still lands, the previous descriptor stays, boot/reconnect resyncs it later, and the failure is logged instead of swallowed. - The live-work pruner can no longer kill a switch mid-dial (#89622's root race): ensureGatewayForProfile / ensureGatewayForAgent lease their entry (activationLeaseUntil, 30s bound) for the duration of the dial and pruneSecondaryGateways spares leased entries. A switch target is not yet active, has no live sessions and holds no request lease, so any prune recompute during a cold pool spawn used to dispose the dialing entry. Bounded lease: an orphaned one self-expires. The agent door logs a non-landing activation (target removed mid-dial) instead of resolving silently, but never fails the switch closed. Live E2E (Electron over CDP, 3 local profiles, cold + warm): 10 sequential switches, rapid two-click interleave (last click wins), 6-click stress run — all land, overlay always clears, fresh pool backends spawn on cold switches. Tests: gateway-activation-prune-lease suite (mid-dial prune survival on both doors, lease release, lease expiry); the invalidated-registry-identity pin updated for the concurrent (read-only) descriptor probe.
૮ >ﻌ< ა ci reviewran on c69a0c2 — fix(desktop): atomic profile-switch publication v2 — fail op
|
|
This PR and #89690 address complementary failure modes, and both are needed for the complete release-build fix. This branch protects a dialing target from the live-work pruner and makes profile/connection publication atomic. However, its current head still pins I reproduced that behavior independently: the target descriptor and authenticated WebSocket connection succeeded, but the release bundle omitted the publication callback and the Desktop remained on the previous connection descriptor. Therefore #89797 fixes the activation/pruner race, while #89690 is still required to ensure the new |
Summary
Re-lands what the reverted atomic-publish series (#89483 → reverted in #89785) was trying to achieve — mode-safe, atomic profile-switch publication for #46651 — without the fail-closed decline path that killed profile clicks (#89622), and fixes the underlying pruner race the original series exposed. Verified flawless in live Electron use before this PR was opened.
Two changes on top of the restored (working) baseline:
$activeGatewayProfile+$connectionin one nanostoresbatch()frame — no request or plugin mode-listener can observe the new gateway beside the old profile's descriptor. Unlike the reverted series, a failed descriptor lookup fails open: the switch still lands, the previous descriptor stays (boot/reconnect resyncs later), and the failure is logged instead of swallowed.ensureGatewayForProfile/ensureGatewayForAgentlease their entry (activationLeaseUntil, 30s bound) for the duration of the dial;pruneSecondaryGatewaysspares leased entries. A switch target is not yet active, has no live sessions, and holds no request lease, so a prune recompute during a cold pool spawn used to dispose the dialing entry. An orphaned lease self-expires.The agent door logs a non-landing activation (target removed mid-dial) instead of resolving silently — observable, but never fail-closed.
Changes
store/gateway.ts:activationLeaseUntilon Secondary; both ensure doors lease during the dial and release when it settles; pruner spares live leases (bounded).store/profile.ts: concurrent descriptor resolve + singlebatch()publication frame on both switch doors; fail-open descriptor contract documented with an explicit do-not-reintroduce-fail-closed warning; swallowed failures nowconsole.warn.gateway-activation-prune-lease.test.ts(mid-dial prune survival on both doors, lease release, lease expiry); one pin updated for the concurrent read-only descriptor probe.Validation
tsc -p ./ eslint on touched filesLive E2E = real Electron app over CDP, isolated
HERMES_HOME, backend pinned to this branch. The reverted series failed this exact harness (every switch declined); the pre-series baseline passed it; this PR passes it with the atomic-publication guarantees added back.Infographic