Skip to content

fix(cli): restart managed dashboard service after update - #39166

Merged
austinpickett merged 2 commits into
NousResearch:mainfrom
andyylin:fix/update-restart-systemd-dashboard
Jul 22, 2026
Merged

fix(cli): restart managed dashboard service after update#39166
austinpickett merged 2 commits into
NousResearch:mainfrom
andyylin:fix/update-restart-systemd-dashboard

Conversation

@andyylin

@andyylin andyylin commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • restart a detected hermes-dashboard.service through systemd during hermes update
  • avoid raw-killing a systemd-owned dashboard PID, which systemd records as a clean stop and does not restart under Restart=on-failure
  • preserve existing manual stale-dashboard cleanup for non-managed dashboard processes and hermes dashboard --stop

Context

The stale-dashboard cleanup added for hermes update is right for manually launched dashboards, but it is the wrong primitive when the dashboard is supervised by systemd.

If update sends SIGTERM directly to the service main PID, systemd reports the unit as cleanly deactivated. With Restart=on-failure, the dashboard stays down after a successful update even though it was configured as a durable service.

This is separate from:

Test plan

  • /home/pi/.hermes/hermes-agent/venv/bin/python -m pytest tests/hermes_cli/test_update_stale_dashboard.py -q -o 'addopts='
  • git diff --check upstream/main..HEAD

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard labels Jun 4, 2026
@andyylin
andyylin force-pushed the fix/update-restart-systemd-dashboard branch from dc41497 to 1a8b0d8 Compare June 5, 2026 00:16
@pasevin

pasevin commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Independently verified this is a real problem.

Reproduction: My dashboard runs as hermes-dashboard.service (systemd user unit). When hermes update calls _kill_stale_dashboard_processes(), it SIGTERMs the main PID directly. systemd interprets this as a clean stop — with Restart=on-failure, the service stays down after a successful update. The user is left with no dashboard until they manually run systemctl --user restart hermes-dashboard.

Why this matters alongside #44165: The detection gap (PR #44165, issue #44035) and this service-restart gap compound. If the dashboard is launched with -p <profile>, the detection step fails first — so the PID is never found, never killed, and never restarted. It just keeps serving stale code indefinitely. Both fixes are needed: #44165 makes detection work, this PR makes the restart service-aware.

Verified locally: After manually restarting my dashboard service post-update, the API correctly reported the new version. The systemctl restart approach this PR uses is the right primitive — it preserves the launch args from the unit file and lets systemd manage the lifecycle, rather than killing the PID and losing the restart policy.

One note: the _kill_stale_dashboard_processes extra_exclude_pids parameter (or equivalent) is needed so service-restarted PIDs are not double-killed in the subsequent non-service cleanup pass. The PR description mentions preserving existing cleanup for non-managed processes, which is the right framing.

@pasevin pasevin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for targeting the correct failure mode: current main still reaches raw SIGTERM for dashboard PIDs in hermes_cli/main.py:6164-6168 after update (:11042).

Problems

  • hermes_cli/main.py:7816 builds only system-scope systemctl commands, and :7852 restarts only system-scope units. The reported reproduction in the PR discussion is a systemctl --user unit; that unit is not discovered here, so _restart_managed_dashboard_service() returns false and the existing raw-kill path still runs.
  • The new tests at tests/hermes_cli/test_update_stale_dashboard.py:348-364 assert only plain systemctl calls, so this user-scope gap is untested.

Suggested changes

  • Probe/restart user and system scopes deliberately, reusing the scope-aware convention in hermes_cli/gateway.py:1999-2019 where appropriate.
  • Add a systemctl --user regression case and retain a system-scope case if both are supported.

Automated hermes-sweeper review.

Comment thread hermes_cli/main.py

def _systemctl(*args: str, timeout: int = 10) -> subprocess.CompletedProcess:
return subprocess.run(
["systemctl", *args],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The reported reproduction is a systemctl --user unit, but this helper always selects the system manager. It will not discover or restart ~/.config/systemd/user/hermes-dashboard.service, then returns False and the caller still SIGTERMs the dashboard PID. Probe/restart the user scope (and system scope if intended) explicitly.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 14, 2026

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hermes Agent Review — Approve

Verified against PR head 9a11529c3. Mergeable/clean, all required CI green.

The July 14 blocking concern (helper only probed the system scope, so a systemctl --user dashboard unit was never discovered and the raw-kill path still ran) is fixed by the July 19 commit. _restart_managed_dashboard_service now probes (("--user",), ()), pins the discovered scope for is-active/is-enabled/restart, and only appends the sudo -n fallback for system-scope units so a user unit never prompts for sudo or leaks to the system manager.

Confirmed the behavior directly by driving the real helper with mocked systemctl:

  • user unit present -> restarted via systemctl --user, no sudo, no system-scope call
  • user probe empty, system unit present -> falls back to system scope
  • no unit anywhere -> returns False so the caller still stops a manually-started dashboard
  • win32 -> short-circuits False

Correctness notes:

  • Returning True on a printed restart failure is intentional and correct: it blocks the os.kill fallback so systemd doesn't record a clean stop that defeats Restart=on-failure. The failure path prints the manual fix.
  • The safety rule from #30271 is preserved: a failed Node refresh still leaves the running dashboard untouched.
  • New tests cover user-scope success, user-scope failure (no sudo/system fallback), and restart-failure-does-not-raw-kill.

Windows note: the 8 TestFindStaleDashboardPids failures I saw locally reproduce on main too (POSIX ps path), unrelated to this PR. The new systemd tests are correctly gated under TestKillStaleDashboardPosix (skipif win32) and run green on the Linux CI slices.

The prior lgtm approve (June 21) predates these commits and the user-scope fix, so it's stale; this review supersedes it.

@austinpickett
austinpickett merged commit 8967e73 into NousResearch:main Jul 22, 2026
33 checks passed
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
…h#39166)

* Keep systemd dashboard alive after update

* fix: restart managed dashboard in owning systemd scope
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 P3 Low — cosmetic, nice to have 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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants