Skip to content

fix(update): skip concurrent-instance abort when only the gateway is running - #37039

Closed
damadorPL wants to merge 1 commit into
NousResearch:mainfrom
damadorPL:fix/update-concurrent-gateway-gate
Closed

damadorPL wants to merge 1 commit into
NousResearch:mainfrom
damadorPL:fix/update-concurrent-gateway-gate

Conversation

@damadorPL

Copy link
Copy Markdown

Bug Description

On Windows, hermes update aborts with exit code 2 and a wall of "Another
hermes.exe is running" guidance whenever any other process whose .exe is
the venv hermes.exe or hermes-gateway.exe shim is running — including
the gateway itself, which the post-update kill+restart block at the bottom
of cmd_update is going to reap anyway.

This forces every Windows user with a running gateway into a manual
taskkill /F /IM hermes.exe dance before every hermes update, even
though the post-update sweep already handles the gateway cleanly. The
user's only escape is --force, which suppresses the message but
doesn't change the underlying redundancy.

Related: the issue #26670 the pre-update gate was added to address
(WinError 32 on the venv shim during pip install -e .) is NOT a
gateway problem — the _quarantine_running_hermes_exe retry /
MoveFileEx-reboot-deferred fallback added at the same time handles
that case for the gateway too. So skipping the abort for gateway-only
matches doesn't reintroduce the original lock failure: the quarantine
step still renames the live shim before uv writes a replacement.

Root Cause

hermes_cli/main.py:9300 calls _detect_concurrent_hermes_instances,
which lists every process whose executable is one of the venv
shims (hermes.exe, hermes-gateway.exe). It then prints the full
list and sys.exit(2) regardless of whether the matches are all
gateways (which the post-update sweep will handle) or genuinely
unrelated processes (TUI shell, Hermes Desktop backend, a separate
REPL in another terminal).

The post-update kill+restart block at lines 9994-10500 matches
gateways by command line — "hermes gateway", "hermes.exe gateway", "hermes-gateway.exe", "hermes_cli.main gateway",
"gateway/run.py" — and correctly excludes the calling process and
its ancestor chain. So the post-update path is already safe; only the
pre-update gate is over-broad.

Fix

Add a small classifier that mirrors the post-update sweep's gateway
patterns, and gate the pre-update abort on the non-gateway
subset of matches. If every concurrent match is a gateway, the
update proceeds and the post-update block reaps the gateway PID. If
anything else is in the list, the existing abort message and
--force escape hatch remain unchanged.

New helpers (in hermes_cli/main.py):

  • _classify_concurrent_instance(pid) — reads the pid's cmdline via
    psutil and returns "gateway", "non-gateway", or "unknown"
    (cmdline unreadable / access denied / psutil unavailable). The
    classifier mirrors the substring markers in
    hermes_cli/gateway.py:_scan_gateway_pids so the two scans match
    the same set of PIDs.
  • _filter_non_gateway_concurrent_instances(matches) — returns the
    subset of concurrent-instance matches whose pid is not a
    gateway.

The pre-update gate at line 9300 now calls the filter and aborts only
on the non-gateway subset:

if concurrent:
    non_gateway = _filter_non_gateway_concurrent_instances(concurrent)
    if non_gateway:
        print(_format_concurrent_instances_message(non_gateway, scripts_dir))
        sys.exit(2)

Unknown-classified pids (psutil can't read them) bucket as
non-gateway — the gate errs on the side of the existing abort, so we
never silently kill a process we couldn't positively identify.

How to Verify

  1. Start a gateway: hermes gateway run (or start it via the service
    manager, on Linux/macOS).
  2. Run hermes update from a different terminal.
  3. Before this fix: exits with code 2, prints "Another hermes.exe is
    running" listing the gateway PIDs, user has to taskkill manually.
  4. After this fix: update proceeds. The gateway is killed by the
    post-update block and respawned by the existing watcher logic
    (launch_detached_profile_gateway_restart).
  5. With a TUI / Desktop / unrelated REPL also running, the gate
    still aborts and lists only the non-gateway matches.

Test Plan

  • Added 8 new tests in
    tests/hermes_cli/test_update_concurrent_quarantine.py:
    • 5 unit tests for the new helpers (gateway cmdline
      recognition across launcher types, non-gateway cmdline
      recognition, psutil error → unknown, no-psutil → unknown,
      mixed-list filter splitting, gateway-only → empty filter).
    • 2 cmd_update integration tests: the user's bug
      (test_cmd_update_skips_abort_when_only_concurrent_is_gateway)
      and the unchanged behaviour for non-gateway matches
      (test_cmd_update_still_aborts_on_non_gateway_concurrent).
  • Existing 18 tests in test_update_concurrent_quarantine.py
    still pass.
  • Other unrelated test failures on this Windows host
    (test_update_autostash.py,
    test_update_stale_dashboard.py,
    test_cmd_update.py::test_update_on_fork_*) are pre-existing
    and confirmed against main before this change. They are
    caused by Windows-specific wmic/ps quirks, not by this fix.

Risk Assessment

Low — the change is narrowly scoped:

  • The post-update kill+restart block is unchanged; the only
    difference is whether the pre-update gate fires for gateway-only
    matches.
  • The classifier uses the same substring patterns as
    _scan_gateway_pids in hermes_cli/gateway.py, so a pid classified
    as "gateway" here is the same set of PIDs the post-update sweep
    will reap. Symmetry by construction.
  • Unrelated concurrent instances (Desktop backend, TUI, plain REPL)
    still trigger the existing abort with no behaviour change.
  • unknown (psutil can't read cmdline) defaults to non-gateway, so
    we never silently kill a process we couldn't positively identify.

…running

On Windows, `hermes update` aborts with exit code 2 whenever any other
process whose `.exe` is the venv `hermes.exe` or `hermes-gateway.exe`
shim is running — including the gateway itself, which the post-update
kill+restart block is going to reap anyway. The user is forced to
`taskkill /F /IM hermes.exe` every running instance and retry, even
though the update would have worked fine: the post-update sweep
matches gateway PIDs by command line ("hermes gateway" / "hermes.exe
gateway" / etc.), not by the venv shim path, and it correctly excludes
the calling process and its ancestor chain.

The fix classifies each concurrent-instance match by reading its
cmdline through psutil and comparing it to the same gateway-marker
substrings the post-update sweep uses. If every concurrent match is a
gateway, the update proceeds; the gateway PID is reaped by the
post-update block a few seconds later. Unrelated concurrent instances
(TUI shell, Hermes Desktop backend child, a plain REPL) still trigger
the existing abort message, since those aren't covered by the
post-update sweep.

Files changed:
  - hermes_cli/main.py: add _classify_concurrent_instance() and
    _filter_non_gateway_concurrent_instances(); gate the pre-update
    concurrent check on the non-gateway subset only.
  - tests/hermes_cli/test_update_concurrent_quarantine.py: add
    classification, filter, and end-to-end cmd_update tests covering
    both the gateway-only (skip-abort) and mixed (still-abort) paths.
Copilot AI review requested due to automatic review settings June 1, 2026 22:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds logic to relax the Windows pre-update concurrent-instance gate so hermes update can proceed when the only other running Hermes process is the gateway (which is already handled by the post-update kill+restart flow).

Changes:

  • Introduces _classify_concurrent_instance and _filter_non_gateway_concurrent_instances to identify and ignore gateway-only concurrent processes.
  • Updates the Windows concurrent-instance abort gate in _cmd_update_impl to consider only non-gateway processes.
  • Adds unit/integration-style tests covering classification, filtering, and the updated gate behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
hermes_cli/main.py Adds gateway-aware classification/filtering and updates the Windows concurrent-instance abort gate accordingly.
tests/hermes_cli/test_update_concurrent_quarantine.py Adds tests for the new helper functions and for the relaxed/update gate behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +617 to +625
class _BrokenProc:
def cmdline(self): # noqa: D401 — psutil Process duck type
raise psutil.AccessDenied(pid=1234)

fake = types.SimpleNamespace(Process=lambda pid: _BrokenProc())
import psutil as _real_psutil
with patch.dict(sys.modules, {"psutil": _real_psutil}):
result = _classify_concurrent_instance(1234)
assert result == "unknown"
Comment thread hermes_cli/main.py
Comment on lines +8269 to +8276
gateway_markers = (
"hermes_cli.main gateway",
"hermes_cli/main.py gateway",
"hermes gateway",
"hermes.exe gateway",
"hermes-gateway.exe",
"gateway/run.py",
)
@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists labels Jun 2, 2026
@teknium1

Copy link
Copy Markdown
Collaborator

Thanks for isolating the Windows update gate; the underlying issue remains on current main. hermes_cli/main.py:9481-9484 still exits on every concurrent shim before _pause_windows_gateways_for_update() runs at hermes_cli/main.py:9490.

Problems

  • The PR's duplicated substring matcher in hermes_cli/main.py:8276 is not equivalent to current gateway discovery. gateway/status.py:221-297 now tokenizes and accepts only a real gateway runtime command; the PR would treat hermes.exe gateway status as a gateway.
  • tests/hermes_cli/test_update_concurrent_quarantine.py:625 patches real psutil, not the constructed fake, so its AccessDenied branch is not exercised.
  • The new cmd_update tests mock _filter_non_gateway_concurrent_instances, leaving the real gate-to-classifier integration untested.

Suggested changes

  • Reuse the current gateway command-line predicate rather than maintaining marker substrings, and add a status-command negative case.
  • Fix the psutil stub and test the real filter through the update gate.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users area/install-update Installer, updater, packaging, wheels, doctor labels Jul 13, 2026
teknium1 pushed a commit that referenced this pull request Aug 25, 2026
… update (#37039)

On Windows, the pre-update concurrent-instance gate aborted with exit 2
whenever ANY other process held the venv hermes.exe shim — including the
gateway itself, which _pause_windows_gateways_for_update() stops moments
later and the post-update restart phase brings back. Users with a running
gateway were forced into a manual taskkill dance before every update.

The gate now filters gateway runtimes out of the abort list and proceeds
when nothing else is concurrent. Classification delegates to
_is_pausable_gateway -> gateway.status.looks_like_gateway_command_line
(the canonical shlex-tokenized, profile-selector-aware matcher shared by
the Desktop preflight exemption and the venv-holder guard fallback), so
the gate's exemption and the pause machinery cannot drift apart. Anything
not positively identified as a gateway — REPLs, dashboard, Desktop
backend children, gateway MANAGEMENT commands like 'gateway status',
unreadable cmdlines — still aborts exactly as before, and the abort
message now lists only the PIDs that are actually the user's problem.

Surgical reapply of PR #37039 by @damadorPL onto current main (the gate
moved from hermes_cli/main.py to hermes_cli/update_cmd.py in the main.py
decomposition); his substring classifier was replaced with the canonical
matcher, which also fixes the 'hermes gateway status' misclassification
flagged in review.

Co-authored-by: Hermes <hermes@nousresearch.com>
@teknium1

Copy link
Copy Markdown
Collaborator

Merged via #95059 (rebase merge, commit 36f1423) — your commit cherry-picked onto current main with your authorship preserved in git log. The gate had moved from hermes_cli/main.py into hermes_cli/update_cmd.py in the decomposition since you opened this, so it landed as a surgical reapply of your design: gateway-only concurrent matches no longer abort hermes update; everything else still refuses exactly as before.

One change from your original: the classifier delegates to the canonical gateway.status.looks_like_gateway_command_line matcher (via _is_pausable_gateway) instead of the substring list, which fixes the hermes gateway status misclassification flagged in review and keeps the gate in lockstep with the pause machinery. Also rode along: the abort message now lists only non-gateway PIDs, so users are no longer told to taskkill a gateway the updater handles itself.

Verified live on windows-latest with real spawned processes (11/11), plus an A/B run proving the old refusal fires on merge-base. Thanks for the fix and the thorough root-cause writeup — the post-update-sweep symmetry argument was exactly right.

@teknium1 teknium1 closed this Aug 25, 2026
teknium1 pushed a commit that referenced this pull request Aug 26, 2026
…v mutation

On Windows installs where the gateway runs as an SCM service (WinSW,
NSSM, sc.exe create), the existing pause machinery kills the gateway
process directly — and the service wrapper's failure ladder resurrects
it within seconds, re-taking the venv file locks mid-update. The update
then dies partway through dependency sync with access-denied errors.

This extends _pause_windows_gateways_for_update() to detect when a
gateway's process tree is owned by a running SCM service, and to stop
the SERVICE through sc.exe instead of killing the child:

- gateway/status.py: expose service-ownership discovery for gateway
  runtimes (find_windows_gateway_services maps validated gateway PIDs
  through process ancestry to running SCM service PIDs, with
  create-time identity checks against PID reuse).
- hermes_cli/update_cmd.py: stop verified services via sc.exe before
  venv mutation and restart them afterward. Stops wait for a stable
  SCM 'stopped' state AND for the original descendant processes to
  exit (service 'Stopped' is not proof the child released its
  handles). Failure to prove ownership, stop a service, or restart it
  fails closed; rollback restores attempted services, and rollback
  failures are surfaced rather than swallowed.
- Fail-closed throughout: unreadable identities, ambiguous ancestry,
  or a service that will not reach a stable state abort the update
  before any file mutation.

Complements #37039 (gateway-only concurrent instances no longer abort):
that fix lets the update proceed past the gate; this one makes the
pause actually stick when the gateway is service-supervised.

Note: tests/gateway/test_status.py::TestReadProcessCmdlinePsFallback::
test_ps_fallback_when_proc_unavailable fails on Windows on current main
before this change as well (POSIX ps fallback asserted on a platform
without it); all other touched suites pass (155 passed, 5 skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Finn763 pushed a commit to Finn763/hermes-agent that referenced this pull request Aug 27, 2026
…v mutation

On Windows installs where the gateway runs as an SCM service (WinSW,
NSSM, sc.exe create), the existing pause machinery kills the gateway
process directly — and the service wrapper's failure ladder resurrects
it within seconds, re-taking the venv file locks mid-update. The update
then dies partway through dependency sync with access-denied errors.

This extends _pause_windows_gateways_for_update() to detect when a
gateway's process tree is owned by a running SCM service, and to stop
the SERVICE through sc.exe instead of killing the child:

- gateway/status.py: expose service-ownership discovery for gateway
  runtimes (find_windows_gateway_services maps validated gateway PIDs
  through process ancestry to running SCM service PIDs, with
  create-time identity checks against PID reuse).
- hermes_cli/update_cmd.py: stop verified services via sc.exe before
  venv mutation and restart them afterward. Stops wait for a stable
  SCM 'stopped' state AND for the original descendant processes to
  exit (service 'Stopped' is not proof the child released its
  handles). Failure to prove ownership, stop a service, or restart it
  fails closed; rollback restores attempted services, and rollback
  failures are surfaced rather than swallowed.
- Fail-closed throughout: unreadable identities, ambiguous ancestry,
  or a service that will not reach a stable state abort the update
  before any file mutation.

Complements NousResearch#37039 (gateway-only concurrent instances no longer abort):
that fix lets the update proceed past the gate; this one makes the
pause actually stick when the gateway is service-supervised.

Note: tests/gateway/test_status.py::TestReadProcessCmdlinePsFallback::
test_ps_fallback_when_proc_unavailable fails on Windows on current main
before this change as well (POSIX ps fallback asserted on a platform
without it); all other touched suites pass (155 passed, 5 skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
and7777 pushed a commit to and7777/hermes-agent that referenced this pull request Aug 27, 2026
… update (NousResearch#37039)

On Windows, the pre-update concurrent-instance gate aborted with exit 2
whenever ANY other process held the venv hermes.exe shim — including the
gateway itself, which _pause_windows_gateways_for_update() stops moments
later and the post-update restart phase brings back. Users with a running
gateway were forced into a manual taskkill dance before every update.

The gate now filters gateway runtimes out of the abort list and proceeds
when nothing else is concurrent. Classification delegates to
_is_pausable_gateway -> gateway.status.looks_like_gateway_command_line
(the canonical shlex-tokenized, profile-selector-aware matcher shared by
the Desktop preflight exemption and the venv-holder guard fallback), so
the gate's exemption and the pause machinery cannot drift apart. Anything
not positively identified as a gateway — REPLs, dashboard, Desktop
backend children, gateway MANAGEMENT commands like 'gateway status',
unreadable cmdlines — still aborts exactly as before, and the abort
message now lists only the PIDs that are actually the user's problem.

Surgical reapply of PR NousResearch#37039 by @damadorPL onto current main (the gate
moved from hermes_cli/main.py to hermes_cli/update_cmd.py in the main.py
decomposition); his substring classifier was replaced with the canonical
matcher, which also fixes the 'hermes gateway status' misclassification
flagged in review.

Co-authored-by: Hermes <hermes@nousresearch.com>
and7777 pushed a commit to and7777/hermes-agent that referenced this pull request Aug 27, 2026
…v mutation

On Windows installs where the gateway runs as an SCM service (WinSW,
NSSM, sc.exe create), the existing pause machinery kills the gateway
process directly — and the service wrapper's failure ladder resurrects
it within seconds, re-taking the venv file locks mid-update. The update
then dies partway through dependency sync with access-denied errors.

This extends _pause_windows_gateways_for_update() to detect when a
gateway's process tree is owned by a running SCM service, and to stop
the SERVICE through sc.exe instead of killing the child:

- gateway/status.py: expose service-ownership discovery for gateway
  runtimes (find_windows_gateway_services maps validated gateway PIDs
  through process ancestry to running SCM service PIDs, with
  create-time identity checks against PID reuse).
- hermes_cli/update_cmd.py: stop verified services via sc.exe before
  venv mutation and restart them afterward. Stops wait for a stable
  SCM 'stopped' state AND for the original descendant processes to
  exit (service 'Stopped' is not proof the child released its
  handles). Failure to prove ownership, stop a service, or restart it
  fails closed; rollback restores attempted services, and rollback
  failures are surfaced rather than swallowed.
- Fail-closed throughout: unreadable identities, ambiguous ancestry,
  or a service that will not reach a stable state abort the update
  before any file mutation.

Complements NousResearch#37039 (gateway-only concurrent instances no longer abort):
that fix lets the update proceed past the gate; this one makes the
pause actually stick when the gateway is service-supervised.

Note: tests/gateway/test_status.py::TestReadProcessCmdlinePsFallback::
test_ps_fallback_when_proc_unavailable fails on Windows on current main
before this change as well (POSIX ps fallback asserted on a platform
without it); all other touched suites pass (155 passed, 5 skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RecursiveIntell added a commit to RecursiveIntell/Ares that referenced this pull request Aug 31, 2026
…dards, update gate, llms.txt URL)

MCP profile scope: scope_tool_arguments receives only real dict configs.
Test doubles (SimpleNamespace without _config, MagicMock auto-attrs)
were crashing or failing closed on a scope policy they never had;
production dicts keep the fail-closed malformed-scope rule.

Skills standards: rewrite 6 descriptions to <=60 chars with terminal
period, fix humanizer tags placement, replace dangling writing-plans
related_skills references. Suite now 1202/1202.

Update gate: apply the NousResearch#37039 gateway exception to the second Windows
concurrent-instance gate too, matching the first gate and the tests.

llms.txt: SITE_BASE was repointed to a GitHub Pages URL that 404s
(has_pages false); revert to the live serving host the skill documents.

Test fixtures: model_tools no-listener test neutralizes first-party
observability (stack_monitor defaults enabled, so post_tool_call has a
listener); approval_interrupt assertions include the witness key added
by 615d214.
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
… update (NousResearch#37039)

On Windows, the pre-update concurrent-instance gate aborted with exit 2
whenever ANY other process held the venv hermes.exe shim — including the
gateway itself, which _pause_windows_gateways_for_update() stops moments
later and the post-update restart phase brings back. Users with a running
gateway were forced into a manual taskkill dance before every update.

The gate now filters gateway runtimes out of the abort list and proceeds
when nothing else is concurrent. Classification delegates to
_is_pausable_gateway -> gateway.status.looks_like_gateway_command_line
(the canonical shlex-tokenized, profile-selector-aware matcher shared by
the Desktop preflight exemption and the venv-holder guard fallback), so
the gate's exemption and the pause machinery cannot drift apart. Anything
not positively identified as a gateway — REPLs, dashboard, Desktop
backend children, gateway MANAGEMENT commands like 'gateway status',
unreadable cmdlines — still aborts exactly as before, and the abort
message now lists only the PIDs that are actually the user's problem.

Surgical reapply of PR NousResearch#37039 by @damadorPL onto current main (the gate
moved from hermes_cli/main.py to hermes_cli/update_cmd.py in the main.py
decomposition); his substring classifier was replaced with the canonical
matcher, which also fixes the 'hermes gateway status' misclassification
flagged in review.

Co-authored-by: Hermes <hermes@nousresearch.com>
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
…v mutation

On Windows installs where the gateway runs as an SCM service (WinSW,
NSSM, sc.exe create), the existing pause machinery kills the gateway
process directly — and the service wrapper's failure ladder resurrects
it within seconds, re-taking the venv file locks mid-update. The update
then dies partway through dependency sync with access-denied errors.

This extends _pause_windows_gateways_for_update() to detect when a
gateway's process tree is owned by a running SCM service, and to stop
the SERVICE through sc.exe instead of killing the child:

- gateway/status.py: expose service-ownership discovery for gateway
  runtimes (find_windows_gateway_services maps validated gateway PIDs
  through process ancestry to running SCM service PIDs, with
  create-time identity checks against PID reuse).
- hermes_cli/update_cmd.py: stop verified services via sc.exe before
  venv mutation and restart them afterward. Stops wait for a stable
  SCM 'stopped' state AND for the original descendant processes to
  exit (service 'Stopped' is not proof the child released its
  handles). Failure to prove ownership, stop a service, or restart it
  fails closed; rollback restores attempted services, and rollback
  failures are surfaced rather than swallowed.
- Fail-closed throughout: unreadable identities, ambiguous ancestry,
  or a service that will not reach a stable state abort the update
  before any file mutation.

Complements NousResearch#37039 (gateway-only concurrent instances no longer abort):
that fix lets the update proceed past the gate; this one makes the
pause actually stick when the gateway is service-supervised.

Note: tests/gateway/test_status.py::TestReadProcessCmdlinePsFallback::
test_ps_fallback_when_proc_unavailable fails on Windows on current main
before this change as well (POSIX ps fallback asserted on a platform
without it); all other touched suites pass (155 passed, 5 skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zapabob pushed a commit to zapabob/hermes-agent-windows that referenced this pull request Sep 5, 2026
…v mutation

On Windows installs where the gateway runs as an SCM service (WinSW,
NSSM, sc.exe create), the existing pause machinery kills the gateway
process directly — and the service wrapper's failure ladder resurrects
it within seconds, re-taking the venv file locks mid-update. The update
then dies partway through dependency sync with access-denied errors.

This extends _pause_windows_gateways_for_update() to detect when a
gateway's process tree is owned by a running SCM service, and to stop
the SERVICE through sc.exe instead of killing the child:

- gateway/status.py: expose service-ownership discovery for gateway
  runtimes (find_windows_gateway_services maps validated gateway PIDs
  through process ancestry to running SCM service PIDs, with
  create-time identity checks against PID reuse).
- hermes_cli/update_cmd.py: stop verified services via sc.exe before
  venv mutation and restart them afterward. Stops wait for a stable
  SCM 'stopped' state AND for the original descendant processes to
  exit (service 'Stopped' is not proof the child released its
  handles). Failure to prove ownership, stop a service, or restart it
  fails closed; rollback restores attempted services, and rollback
  failures are surfaced rather than swallowed.
- Fail-closed throughout: unreadable identities, ambiguous ancestry,
  or a service that will not reach a stable state abort the update
  before any file mutation.

Complements NousResearch#37039 (gateway-only concurrent instances no longer abort):
that fix lets the update proceed past the gate; this one makes the
pause actually stick when the gateway is service-supervised.

Note: tests/gateway/test_status.py::TestReadProcessCmdlinePsFallback::
test_ps_fallback_when_proc_unavailable fails on Windows on current main
before this change as well (POSIX ps fallback asserted on a platform
without it); all other touched suites pass (155 passed, 5 skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kanekoshoyu pushed a commit to kanekoshoyu/hermes-agent-1 that referenced this pull request Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants