Skip to content

fix(cli): quarantine running hermes.exe during update dep-verification repair on Windows - #40409

Merged
OutThisLife merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:fix/windows-update-quarantine-repair-shim
Jun 6, 2026
Merged

fix(cli): quarantine running hermes.exe during update dep-verification repair on Windows#40409
OutThisLife merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:fix/windows-update-quarantine-repair-shim

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Problem

On Windows, hermes update can leave the user with hermes: The term 'hermes' is not recognized as a name of a cmdlet... — the hermes.exe launcher disappears from the venv Scripts/ directory after an otherwise-successful update.

Root cause

hermes update reinstalls deps via _install_python_dependencies_with_optional_fallback, which has a Windows-specific dance (added in #23394): before each editable install it quarantines the running hermes.exe (renames it to hermes.exe.old.<ts>), because Windows blocks REPLACE on a mapped/running executable but allows RENAME. uv/pip then writes a fresh shim at the original path, and the old one is restored if the install fails before writing a replacement. That commit's stated intent was to wrap "every install attempt (primary, base-only fallback, and per-extra retries)."

Later, c136eb4de added _verify_core_dependencies_installed — a post-install belt-and-suspenders check that reinstalls with --reinstall -e . if a declared base dep didn't land. But that repair install calls _run_install_with_heartbeat(...) directly, bypassing the quarantine. --reinstall -e . uninstalls + reinstalls the hermes-agent package, including its entry-point shims. On Windows pip can neither delete nor overwrite the live hermes.exe, and because no quarantine ran there was no .old fallback either → the shim is left missing and hermes drops off PATH.

This matches the reported symptom: the update appears to run twice (the second "Requirement already satisfied" pass is the verification repair), errors on exit, and afterward hermes is gone.

Fix

  • Extract the rename-out-of-the-way / restore-on-failure logic into a reusable _run_quarantined_install(cmd, env, scripts_dir) helper.
  • Route both the primary editable installs (the _install closure is now a thin delegate — behaviorally identical) and the --reinstall -e . verification repair through it. This completes the original fix(windows): unbreak install + update on Windows (3 issues) #23394 invariant: every install that rewrites the shims is quarantined.
  • The per-package repair (--reinstall <specs>) installs only third-party deps (e.g. pathspec), never hermes-agent, so it doesn't touch the shims and is intentionally left unquarantined to keep the change tight.

Off-Windows (scripts_dir=None) the helper is a pure pass-through — no behavior change.

Test

Added test_repair_reinstall_quarantines_running_shim_on_windows: asserts the --reinstall -e . repair calls _quarantine_running_hermes_exe with the venv Scripts dir on Windows. Verified it fails against the old code (quarantine not called) and passes against the fix.

All existing test_verify_core_dependencies (now 8), test_update_concurrent_quarantine + test_update_autostash (48) pass. A full tests/hermes_cli/ update-related selection shows the same 2 pre-existing flaky failures on a clean origin/main baseline (test_recommended_update_command_defaults_to_hermes_update, test_non_interactive_discard_throws_changes_away) — both pass in isolation; unrelated to this change.

Recovery for already-affected users

The package itself installed fine — only the shim is missing:

<venv>\Scripts\python.exe -m pip install -e "<repo>" --force-reinstall --no-deps

(rewrites the shim in seconds, skips the dependency wall).

…n repair on Windows

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard area/config Config system, migrations, profiles labels Jun 6, 2026
@OutThisLife

Copy link
Copy Markdown
Collaborator

Reviewed the diff — clean, surgical, correct. The root cause is exactly right: _verify_core_dependencies_installed's --reinstall -e . repair called _run_install_with_heartbeat directly, bypassing the shim-quarantine the primary path does, and --reinstall -e . rewrites the entry-point shims that Windows can't overwrite on the live hermes.exe. Routing it through _run_quarantined_install closes the #23394 invariant.

Things I checked and like:

  • The _install closure → delegate is behaviorally identical (same BaseException catch, restore-on-failure, re-raise), so the primary path is unchanged.
  • Error propagation preserved: the helper restores then re-raises, so the repair's except subprocess.CalledProcessError still catches/logs as before.
  • Leaving the per-package --reinstall <specs> repair unquarantined is correct — it only installs third-party base deps, never hermes-agent, so it never writes the hermes.exe shim (and _missing_deps() can't return hermes-agent itself).
  • Off-Windows is a pure pass-through; the regression test fails on old code and pins _quarantine_running_hermes_exe is called with the venv Scripts dir.

One suggestion: add a one-line inline note that catching BaseException (not Exception) in _run_quarantined_install is deliberate — it restores the shim even on KeyboardInterrupt/SystemExit mid-install, which is the whole point on Windows. Without the comment a future "tighten to Exception" cleanup would silently reopen the Ctrl-C-leaves-no-launcher window.

Minor (optional): scripts_dir = _venv_scripts_dir() if _is_windows() else None is now computed in both the fallback installer and the repair — fine, just the one bit of duplication the otherwise-DRY extraction reintroduces.

LGTM. 👍

@OutThisLife
OutThisLife merged commit ebed881 into NousResearch:main Jun 6, 2026
23 checks passed
changman pushed a commit to changman/hermes-agent that referenced this pull request Jun 10, 2026
…n repair on Windows (NousResearch#40409)

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
…n repair on Windows (#40409)

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
davidgut1982 pushed a commit to davidgut1982/hermes-agent that referenced this pull request Jun 17, 2026
…n repair on Windows (NousResearch#40409)

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
T02200059 pushed a commit to T02200059/hermes-agent that referenced this pull request Jun 18, 2026
…n repair on Windows (NousResearch#40409)

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…n repair on Windows (NousResearch#40409)

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…n repair on Windows (NousResearch#40409)

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
donbowman pushed a commit to donbowman/hermes-agent that referenced this pull request Jul 13, 2026
…n repair on Windows (NousResearch#40409)

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…n repair on Windows (NousResearch#40409)

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
@kshitijk4poor
kshitijk4poor deleted the fix/windows-update-quarantine-repair-shim branch August 5, 2026 07:08
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…n repair on Windows (NousResearch#40409)

The dependency-verification repair in _verify_core_dependencies_installed
ran 'pip install --reinstall -e .' via _run_install_with_heartbeat directly,
bypassing the Windows shim-quarantine that the primary install path performs.

That reinstall rewrites the entry-point shims, and on Windows the live
hermes.exe is the running process — pip can neither delete nor overwrite it.
With no quarantine, the shim was left missing and 'hermes' dropped off PATH
('hermes' is not recognized... after update).

Extract the rename-out-of-the-way / restore-on-failure logic into a reusable
_run_quarantined_install helper and route both the primary editable installs
and the --reinstall -e . repair through it. The per-package repair installs
only third-party deps (never hermes-agent), so they don't touch the shims and
are left untouched. Add a regression test (fails on old code, passes on new).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants