From 71f3ee09427bb2fe94f24eed7aa10814815d8c38 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Fri, 18 Sep 2026 04:03:35 -0700 Subject: [PATCH] fix(dashboard): pin --stop and the update sweep to scope_home Swapping main.py or update_cmd_maint.py back to origin/main left every PR test green, so the whole home scoping of `hermes dashboard --stop` and the post-update dashboard sweep could regress silently. One test per path now asserts the scan and kill calls carry scope_home = the invoking home (and the empty-scan message names the profile). --- .../test_dashboard_lifecycle_flags.py | 21 +++++++++++++++++++ .../hermes_cli/test_update_stale_dashboard.py | 8 +++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/tests/hermes_cli/test_dashboard_lifecycle_flags.py b/tests/hermes_cli/test_dashboard_lifecycle_flags.py index 1e70187aff..acfabdebad 100644 --- a/tests/hermes_cli/test_dashboard_lifecycle_flags.py +++ b/tests/hermes_cli/test_dashboard_lifecycle_flags.py @@ -100,6 +100,27 @@ class TestDashboardStop: assert "stop" in kwargs["reason"].lower() assert exc.value.code == 0 + def test_stop_scopes_scan_and_kill_to_the_invoking_hermes_home(self, tmp_path, monkeypatch, capsys): + """``--stop`` targets only this profile's backends (#113978): both the pre-check and the + kill run with ``scope_home`` = the invoking home, and an empty scan says so.""" + own_home = tmp_path / "profiles" / "work" + monkeypatch.setenv("HERMES_HOME", str(own_home)) + with patch("hermes_cli.main._find_stale_dashboard_pids", return_value=[12345]) as scan, \ + patch("hermes_cli.dashboard_procs._kill_stale_dashboard_processes", + return_value={"matched": [12345], "killed": [12345], "failed": [], "unrecovered": []}) as kill, \ + pytest.raises(SystemExit): + cmd_dashboard(_ns(stop=True)) + assert scan.call_args.kwargs["scope_home"] == str(own_home) + assert kill.call_args.kwargs["scope_home"] == str(own_home) + + with patch("hermes_cli.main._find_stale_dashboard_pids", return_value=[]), \ + patch("hermes_cli.dashboard_procs._kill_stale_dashboard_processes") as kill, \ + pytest.raises(SystemExit) as exc: + cmd_dashboard(_ns(stop=True)) + kill.assert_not_called() + assert exc.value.code == 0 + assert "for this profile" in capsys.readouterr().out + def test_stop_exits_nonzero_if_kill_leaves_survivors(self): """A pid the kill path could not stop (e.g. permission denied) -> exit 1 so scripts can detect that the stop didn't succeed.""" diff --git a/tests/hermes_cli/test_update_stale_dashboard.py b/tests/hermes_cli/test_update_stale_dashboard.py index 17988a35cc..902c945260 100644 --- a/tests/hermes_cli/test_update_stale_dashboard.py +++ b/tests/hermes_cli/test_update_stale_dashboard.py @@ -320,14 +320,18 @@ class TestBackCompatAlias: class TestDashboardUpdateCleanup: """The git and Windows ZIP update paths share this final cleanup.""" - def test_all_failed_stops_do_not_claim_the_dashboard_was_stopped(self, capsys): + def test_all_failed_stops_do_not_claim_the_dashboard_was_stopped(self, capsys, monkeypatch, tmp_path): + own_home = tmp_path / "profiles" / "work" + monkeypatch.setenv("HERMES_HOME", str(own_home)) with patch( "hermes_cli.main._kill_stale_dashboard_processes", return_value={"matched": [12345], "killed": [], "failed": [(12345, "denied")], "unrecovered": []}, - ): + ) as kill: _finish_dashboard_update_cleanup([]) + # The sweep only touches this home's backends (#113978). + assert kill.call_args.kwargs["scope_home"] == str(own_home) assert "stopped during update" not in capsys.readouterr().out