fix(desktop): seed saved profile before gateway boot - #50529
Conversation
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the saved-profile boot ordering issue. Current main still has the underlying ordering gap: cold boot calls desktop.getConnection() at apps/desktop/src/app/gateway/hooks/use-gateway-boot.ts:432, while adoptPrimaryProfile() is called only after gateway.connect() at line 456. The Electron backend itself selects its primary profile from the saved desktop preference (apps/desktop/electron/main.ts:6424-6429), so the renderer can temporarily retain its default routing state.
Problems
- The added test checks
$activeGatewayProfileonly afterflushAsync()(apps/desktop/src/app/gateway/hooks/use-gateway-boot.test.tsx:234in this PR). It does not prove the required ordering relative todesktop.getConnection().
Suggested changes
- Rebase the idea onto the current
adoptPrimaryProfile()helper introduced byb3bde1fbee, and invoke the appropriate seeding step before the cold-bootgetConnection()call. - Use a deferred
getConnection()test double and assert saved-profile routing before allowing it to resolve.
Automated hermes-sweeper review.
| desktop.profile.get = vi.fn(async () => ({ profile: 'coder' })) | ||
| ;(window as { hermesDesktop?: unknown }).hermesDesktop = desktop | ||
|
|
||
| render(<Harness />) |
There was a problem hiding this comment.
This assertion runs after flushAsync(), so it can pass even if the profile is adopted after getConnection() has already started. Please defer getConnection() and assert the seeded profile before resolving it to lock down the boot-ordering contract.
Cold boot published gatewayReady before adoptPrimaryProfile()'s IPC read
returned: the shared gateway client fires setState('open') before
connect() resolves, while boot only awaited the profile adoption
afterwards. Any profile-scoped fetch triggered by gatewayReady (sessions,
projects) therefore raced the $activeGatewayProfile atom at its initial
'default' -- on a machine whose primary profile is a named one, the first
sidebar population was scoped to the wrong profile. Same ordering family
as NousResearch#50529.
Fix: split the persisted-profile read out of adoptPrimaryProfile() and
apply it before desktop.getConnection(), so the atom, the registry's
primaryProfile, and event tagging all carry the persisted profile before
any connection state can be observed. Applying the profile also aligns
the registry's active pointer (ensureGatewayForProfile fast path):
without that, state events tagged with the adopted primary fail the
activeKey match in reportGatewayState, the 'open' transition is dropped,
and the UI hangs at 'connecting'. HMR-survivor adoption and the
catch->default fallback are preserved.
Tests: the boot-ordering test asserts the profile IPC resolves and the
atom carries the persisted profile before getConnection(), and that the
gateway still publishes 'open' after the socket opens (regression for
the dropped-open hang).
Summary
default.coderDesktop profile.Background / Why
When Hermes Desktop is configured to launch a non-default profile, the main process correctly starts the primary backend with
--profile <name>. The renderer still initializes its active gateway profile asdefaultuntil the connection finishes, which can make boot-time profile-scoped work briefly target the wrong profile.Changes
desktop.profile.get()at the start of gateway boot and seed$activeGatewayProfileplussetPrimaryGatewaybeforedesktop.getConnection().$activeGatewayProfilein the gateway boot test.Test plan
npm run typechecknpm run test:ui -- src/app/gateway/hooks/use-gateway-boot.test.tsx -t 'seeds the primary gateway profile'Risks / Rollback
defaultbehavior.Refs