Skip to content

fix(desktop): allow switching primary profile back to default - #86092

Open
promptprobe wants to merge 1 commit into
NousResearch:mainfrom
promptprobe:fix/desktop-default-primary-profile
Open

fix(desktop): allow switching primary profile back to default#86092
promptprobe wants to merge 1 commit into
NousResearch:mainfrom
promptprobe:fix/desktop-default-primary-profile

Conversation

@promptprobe

Copy link
Copy Markdown

What does this PR do?

Adds an explicit Make primary Desktop profile action to the existing Manage Profiles row menu. This uses the existing hermes:profile:set lifecycle to persist the selection, restart the primary backend under the selected HERMES_HOME, and reload the window.

This action is intentionally separate from ordinary workspace/gateway navigation. selectProfile() continues to switch the live chat/session context without changing the persisted Desktop primary profile.

Related Issue

Fixes #85991

Root Cause

Electron already exposed profile.set(), and the renderer retained switchProfile(), but the Profiles view no longer had a UI action wired to that path. Once active-profile.json contained a named profile, users could navigate the default workspace but could not explicitly restore default as the persisted primary Desktop profile.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • Added the explicit primary-profile action and a Primary for Desktop badge in apps/desktop/src/app/profiles/index.tsx, with localized copy in all Desktop locales.
  • Kept confirmed running-primary state separate from the persisted preference in apps/desktop/src/store/profile.ts, so no-op suppression does not block recovery after a partial restart failure.
  • Factored the existing active-profile.json write → primary teardown → window reload lifecycle into apps/desktop/electron/desktop-primary-profile.ts for direct regression coverage.
  • Extended the existing renderer/profile tests and added focused Electron lifecycle tests.

How to Test

Commands run from apps/desktop:

npx vitest run --project ui src/store/profile.test.ts src/app/profiles/index.test.tsx
npx vitest run --project electron electron/desktop-primary-profile.test.ts
npm run check

Results:

  • Focused renderer/store tests: 25 passed.
  • Focused Electron lifecycle tests: 4 passed.
  • Full npm run check: typecheck passed; lint completed with 0 errors and 88 existing warnings; 3,853 renderer tests passed; 1,059 Electron tests passed with 2 skipped; macOS arm64 app/DMG packaging passed.
  • git diff --check: passed.

Manual reproduction:

  1. Create a named profile such as dev, make it the primary Desktop profile, and confirm Desktop reloads with active-profile.json containing {"profile":"dev"}.
  2. Open Manage Profiles, open the row menu for default, and choose Make primary Desktop profile.
  3. Confirm Desktop reloads under the default/root HERMES_HOME and active-profile.json now contains {"profile":"default"}.
  4. Navigate between profile workspaces from the profile rail and confirm those clicks do not rewrite active-profile.json or restart the primary backend.
  5. Open the current primary profile's row menu and confirm the primary action is disabled.

Regression Safety

Ordinary selectProfile() / gateway workspace switching is unchanged and never calls profile.set(). The change introduces no new config key or migration and does not alter profile credentials, configuration, sessions, or isolation. The Electron path continues to use the existing platform-native app.getPath('userData') location and atomic active-profile.json replacement before primary-backend teardown and reload.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 26.5.2, arm64

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

Not included. The new action reuses the existing Profiles row-menu pattern; focused UI tests cover its enabled, disabled, default, and named-profile states.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) area/profiles Multi-profile isolation, HERMES_HOME scoping sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 14, 2026
@promptprobe
promptprobe marked this pull request as ready for review August 14, 2026 14:53
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference, author can ignore or act on any point.

fix(desktop): allow switching primary profile back to default

  1. apps/desktop/electron/desktop-primary-profile.ts:writeDesktopPrimaryProfile — the new implementation (writeFileSync to a .tmp + renameSync) replaces the previous writeFileAtomic helper. It drops the fsync that made the old write durable, and a failed renameSync (e.g. an AV lock on Windows) leaves a stale .tmp behind. Consider restoring the atomic-write utility (fsync + temp cleanup) so the persist step is at least as robust as the code it replaces.

  2. apps/desktop/src/store/profile.ts:switchProfile — the failure-restore path compares atom state by object identity ($primaryDesktopProfileState.get() === switchingState). If any concurrent store update happens between the two awaited profile.get() calls, the identity check fails and the restore is skipped, leaving a stale { current: null, persisted: target }. Comparing by value (or snapshotting the previous state fields up front) would be more robust against interleaved updates.

  3. applyDesktopPrimaryProfile persists the new profile before tearing down the backend. If teardown fails, the persisted file and the running backend disagree until the retry completes — the retry flow is well tested, but the UI pill can briefly reflect the new profile while the old backend is still running. A status hint when the teardown error surfaces would make that state visible.

  4. Extracting the persistence logic into desktop-primary-profile.ts with deterministic unit tests (including the failed-teardown retry) is a solid refactor, and all locale files were updated with the new keys. No issues there.

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/*) P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop: can't switch back to the 'default' profile once active-profile.json is set

3 participants