fix(profiles): detect a separate-process gateway in profile status - #51709
Merged
Conversation
The dashboard Profiles view showed "Gateway stopped" for a gateway that
is in fact running — while the sidebar status strip and `hermes gateway
status` (CLI) both correctly showed it running. Reported on v0.17.0
running the gateway + dashboard in one Docker container.
Root cause: three liveness surfaces with three detection strengths, all
reading the same `gateway.pid`:
- `hermes gateway status` -> find_gateway_pids() (process-table scan)
- sidebar /api/status -> get_running_pid() + gateway_state.json PID
fallback + health-URL probe
- Profiles view -> _check_gateway_running() = get_running_pid()
ONLY, no fallback
`get_running_pid()` short-circuits to None the moment the runtime lock
(`gateway.lock`) doesn't register as held by the *calling* process —
which is always true when the reader is a separate process from the
gateway (the dashboard is its own s6 service in the container), and also
for any launch-service-managed gateway that left a fresh
`gateway_state.json` but no live PID file. So the Profiles view alone
reported the live gateway as stopped.
Fix: give _check_gateway_running the same fallback the sidebar already
has — after the pid-file/lock check misses, validate the PID recorded in
that profile's gateway_state.json against the live process table via the
existing get_runtime_status_running_pid(). read_runtime_status() gains an
optional path arg so a profile's state file can be read without mutating
the process-global HERMES_HOME (preserving the contextvar-based profile
isolation the dashboard relies on). Backward compatible: every existing
caller passes no argument.
Tests: a regression test that fails pre-fix (live gateway, lock check
returns None -> must still report running) and a guard test that a
'stopped' state file is never reported running even with a live PID.
Contributor
🔎 Lint report:
|
Collaborator
This was referenced Jul 6, 2026
justemu
added a commit
to justemu/hermes-agent
that referenced
this pull request
Jul 10, 2026
Systemd-managed gateway processes show just "hermes" in /proc/PID/cmdline rather than "hermes gateway run". The _gateway_command_subcommand parser failed to identify these processes because it only accepted an explicit "gateway" token before the subcommand. When has_gateway_entry is True but no "gateway" subcommand is found, and the only remaining token is the bare "hermes"/"hermes.exe" entry-point binary, treat it as a gateway run process. The existing _command_line_belongs_to_profile guard (which validates HERMES_HOME in /proc/PID/environ) prevents false positives. This fixes the L2 fallback in _check_gateway_running (introduced in PR NousResearch#51709) that silently failed for systemd-managed profile gateways, causing `hermes profile list/show` to report "Gateway: stopped" for actually-running gateways that lacked a gateway.pid file. Closes NousResearch#20254
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Infographic
Summary
The dashboard Profiles view shows "Gateway stopped" for a gateway that is in fact running, while the sidebar status strip and
hermes gateway status(CLI) both correctly report it running. Reported on v0.17.0 with the gateway + dashboard running in a single Docker container:Root cause
Three liveness surfaces, three detection strengths — all reading the same
gateway.pidunder$HERMES_HOME:hermes gateway status(CLI)find_gateway_pids()— process-table scan/api/statusget_running_pid()+gateway_state.jsonPID fallback + health-URL probe_check_gateway_running()=get_running_pid()only, no fallbackget_running_pid()(gateway/status.py) short-circuits toNonethe moment the runtime lock (gateway.lock) doesn't register as held by the calling process — before it inspects the PID record. That's always the case when the reader is a separate process from the gateway (in the container the dashboard is its own s6 service), and also for any launch-service-managed gateway that left a freshgateway_state.jsonbut no live PID file. So the Profiles view alone reported the live gateway as stopped.Fix
Give
_check_gateway_running()the same fallback the sidebar already has (web_server.py:1854): after the pid-file/lock check misses, validate the PID recorded in that profile'sgateway_state.jsonagainst the live process table via the existingget_runtime_status_running_pid().read_runtime_status()gains an optionalpathargument so a specific profile's state file can be read without mutating the process-globalHERMES_HOME— preserving the contextvar-based profile isolation the dashboard relies on, and avoiding the env-mutation race an earlier approach (#20488) would have introduced. The fallback is also strictly stronger than#20488'sservice_running-only check, which is alwaysFalsein Docker (no systemd/launchd) and would still miss PID 134.Backward compatible: every existing caller of
read_runtime_status()passes no argument.Tests
test_gateway_running_check_falls_back_to_runtime_state— live gateway, pid-file/lock check returnsNone, must still report running. Verified failing on baseline, passing with the fix.test_gateway_running_check_runtime_state_stopped— agateway_state.jsonwith statestoppedis never reported running, even with a live recorded PID.tests/hermes_cli/test_profiles.py(140) + the gateway runtime-status suites all pass.🎨 Infographic is decorative — details are illustrative, not a spec.