Skip to content

Prevent deleted profiles from being recreated by stale dashboards - #49435

Merged
OutThisLife merged 1 commit into
NousResearch:mainfrom
LeonSGP43:fix/her-295-45474-profile-revival
Jul 2, 2026
Merged

Prevent deleted profiles from being recreated by stale dashboards#49435
OutThisLife merged 1 commit into
NousResearch:mainfrom
LeonSGP43:fix/her-295-45474-profile-revival

Conversation

@LeonSGP43

Copy link
Copy Markdown
Contributor

Summary

  • stop ensure_hermes_home() from recreating a missing named profile skeleton
  • keep bootstrapping existing named profile homes unchanged
  • add regression coverage for existing vs missing named profile directories

Testing

@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have labels Jun 20, 2026
@OutThisLife
OutThisLife force-pushed the fix/her-295-45474-profile-revival branch from a8fa97c to d9bf4ea Compare July 2, 2026 20:07
@OutThisLife

Copy link
Copy Markdown
Collaborator

Reviewed and merging (rebased onto current main, one trivial test-file conflict from line drift resolved — both the SOUL.md tests and your two profile tests are kept).

This is the right fix and the right layer. #47368 stacks three root causes, but the headline symptom — "delete silently fails and the profile keeps reappearing / never converges" — is the resurrection, and this guard kills it at the single chokepoint every resurrection path funnels through. ensure_hermes_home() is reached by the CLI (hermes -p <deleted> …), hermes profile list's transitive config load, the gateway multiplex, AND the Desktop-spawned per-profile backend. Refusing to mkdir a named-profile home that doesn't exist means none of them can re-scaffold an empty skeleton, so a deleted profile stays deleted no matter who touches it next.

Verified it doesn't fight legitimate creation: create_profile() makes the directory itself (profile_dir.mkdir(...) / copytree) before anything calls ensure_hermes_home(), so the guard's not home.exists() is already false by then. It only bites the stale-access path — which matches the profiles-are-explicit-islands design intent (named profiles must be created deliberately, never conjured by an errant reader).

Also lines up with the other two root causes rather than overlapping them: this stops the recreation, #52301 stops the Desktop from respawning a backend into the deleted profile, and Bug 1 (the 3.11 onexc TypeError) plus the read-only _make_writable recovery are already on main (so #47452 and #43377 are superseded). With this guard, even without the Desktop-side PR, the backend that gets spawned for a deleted profile now exits cleanly on FileNotFoundError instead of resurrecting the tree.

Validation: tests/hermes_cli/test_config.py + test_profiles.py — 289/289 via scripts/run_tests.sh.

Thanks @LeonSGP43 — clean, minimal, correctly-placed.

@OutThisLife
OutThisLife enabled auto-merge (rebase) July 2, 2026 20:07
@OutThisLife
OutThisLife merged commit 472d751 into NousResearch:main Jul 2, 2026
29 checks passed
teknium1 pushed a commit that referenced this pull request Aug 17, 2026
Two independent bugs let a deleted profile reappear / leave orphaned
resources on next launch:

1. hermes_cli/profiles.py's backend-process scanner required argv[0] to
   resolve to an executable literally named "hermes". Electron's
   pool-backend spawn resolves the hermes console-script shim's path and
   execs it via the interpreter directly (python3 /path/to/hermes ...), so
   argv[0] reports as "python3" and the scanner never matched the running
   backend -- delete removed the profile's files but left its live backend
   process running (still bound to a port via uvicorn), which
   accumulates across repeated delete/recreate cycles.
2. The desktop sidebar's ProfileRail only refreshed its cached profile
   list once, on mount, so a delete/create/rename from another surface
   (another window, or the CLI) left a stale ghost entry until something
   unrelated triggered a refetch. Note: a delete via this window's own
   Manage-Profiles view already refreshes the shared $profiles atom
   ProfileRail subscribes to (confirmed by reading refreshProfiles() and
   handleConfirmDelete()) -- this fix only covers the cross-window/cross-
   process staleness gap, not a duplicate of the already-merged
   #57329's Manage-Profiles rail-refresh work.

Fix 1: recognize a python-interpreter argv[0] exec'ing a hermes-named
console-script shim via argv[1]. Fix 2: refresh the profile list on window
focus/visibilitychange, matching the existing pattern used elsewhere in
the sidebar (sidebar/index.tsx, use-background-sync.ts, star-map.tsx,
use-gateway-boot.ts all use the same focus+visibilitychange pattern).

## Related work already on main

PR #57329 (merged) fixed the *headline* symptom from issue #52279
(deleted profile respawns) via a different, non-overlapping mechanism:
routing profile-delete through the primary backend instead of spawning a
fresh pool backend, plus a separate recreation guard in
ensure_hermes_home() (#49435, merged) that makes a backend spawned into a
deleted profile's directory raise FileNotFoundError instead of silently
recreating it.

This PR is NOT a duplicate of that fix. Verified: even with both of those
merged, a backend process that survives because of gap #1 above still
holds a bound port via uvicorn -- it just can no longer resurrect the
profile directory. That's real resource-hygiene, not a symptom already
covered. Gap #2 touches a different file/component (ProfileRail /
profile-switcher.tsx) than #57329's rail-refresh half (which touched the
Manage-Profiles view's own $profiles.ts / index.tsx) and covers a
distinct staleness path (cross-window/cross-process, not same-window
delete-then-refresh).

Tests: tests/hermes_cli/test_profiles.py -- 156 passed (existing +
regression coverage for the argv[0] python-interpreter detection case).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lisajlau pushed a commit to lisajlau/hermes-agent that referenced this pull request Aug 20, 2026
Two independent bugs let a deleted profile reappear / leave orphaned
resources on next launch:

1. hermes_cli/profiles.py's backend-process scanner required argv[0] to
   resolve to an executable literally named "hermes". Electron's
   pool-backend spawn resolves the hermes console-script shim's path and
   execs it via the interpreter directly (python3 /path/to/hermes ...), so
   argv[0] reports as "python3" and the scanner never matched the running
   backend -- delete removed the profile's files but left its live backend
   process running (still bound to a port via uvicorn), which
   accumulates across repeated delete/recreate cycles.
2. The desktop sidebar's ProfileRail only refreshed its cached profile
   list once, on mount, so a delete/create/rename from another surface
   (another window, or the CLI) left a stale ghost entry until something
   unrelated triggered a refetch. Note: a delete via this window's own
   Manage-Profiles view already refreshes the shared $profiles atom
   ProfileRail subscribes to (confirmed by reading refreshProfiles() and
   handleConfirmDelete()) -- this fix only covers the cross-window/cross-
   process staleness gap, not a duplicate of the already-merged
   NousResearch#57329's Manage-Profiles rail-refresh work.

Fix 1: recognize a python-interpreter argv[0] exec'ing a hermes-named
console-script shim via argv[1]. Fix 2: refresh the profile list on window
focus/visibilitychange, matching the existing pattern used elsewhere in
the sidebar (sidebar/index.tsx, use-background-sync.ts, star-map.tsx,
use-gateway-boot.ts all use the same focus+visibilitychange pattern).

## Related work already on main

PR NousResearch#57329 (merged) fixed the *headline* symptom from issue NousResearch#52279
(deleted profile respawns) via a different, non-overlapping mechanism:
routing profile-delete through the primary backend instead of spawning a
fresh pool backend, plus a separate recreation guard in
ensure_hermes_home() (NousResearch#49435, merged) that makes a backend spawned into a
deleted profile's directory raise FileNotFoundError instead of silently
recreating it.

This PR is NOT a duplicate of that fix. Verified: even with both of those
merged, a backend process that survives because of gap #1 above still
holds a bound port via uvicorn -- it just can no longer resurrect the
profile directory. That's real resource-hygiene, not a symptom already
covered. Gap NousResearch#2 touches a different file/component (ProfileRail /
profile-switcher.tsx) than NousResearch#57329's rail-refresh half (which touched the
Manage-Profiles view's own $profiles.ts / index.tsx) and covers a
distinct staleness path (cross-window/cross-process, not same-window
delete-then-refresh).

Tests: tests/hermes_cli/test_profiles.py -- 156 passed (existing +
regression coverage for the argv[0] python-interpreter detection case).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants