Skip to content

fix(desktop): seed saved profile before gateway boot - #50529

Open
soichiyo wants to merge 1 commit into
NousResearch:mainfrom
soichiyo:fix/desktop-coder-profile-boot
Open

fix(desktop): seed saved profile before gateway boot#50529
soichiyo wants to merge 1 commit into
NousResearch:mainfrom
soichiyo:fix/desktop-coder-profile-boot

Conversation

@soichiyo

Copy link
Copy Markdown
Contributor

Summary

  • Seed the Desktop renderer's active gateway profile from the saved Desktop profile preference before the primary connection starts.
  • Keep the primary gateway registry aligned with the saved profile so boot-time requests do not briefly assume default.
  • Add a focused regression test for a saved coder Desktop 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 as default until the connection finishes, which can make boot-time profile-scoped work briefly target the wrong profile.

Changes

  • Read desktop.profile.get() at the start of gateway boot and seed $activeGatewayProfile plus setPrimaryGateway before desktop.getConnection().
  • Reuse the same preference seeding after connect so reconnect routing stays aligned.
  • Reset and assert $activeGatewayProfile in the gateway boot test.

Test plan

  • npm run typecheck
  • npm run test:ui -- src/app/gateway/hooks/use-gateway-boot.test.tsx -t 'seeds the primary gateway profile'

Risks / Rollback

  • 想定リスク: malformed or unavailable Desktop profile preference falls back to the existing default behavior.
  • ロールバック手順: revert this PR.

Refs

  • Related: Desktop non-default profile boot routing

@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) and removed comp/gateway Gateway runner, session dispatch, delivery comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Jun 22, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 $activeGatewayProfile only after flushAsync() (apps/desktop/src/app/gateway/hooks/use-gateway-boot.test.tsx:234 in this PR). It does not prove the required ordering relative to desktop.getConnection().

Suggested changes

  • Rebase the idea onto the current adoptPrimaryProfile() helper introduced by b3bde1fbee, and invoke the appropriate seeding step before the cold-boot getConnection() 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 />)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/profiles Multi-profile isolation, HERMES_HOME scoping labels Jul 15, 2026
weddle added a commit to weddle/hermes-agent that referenced this pull request Jul 31, 2026
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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants