diff --git a/hermes_cli/main.py b/hermes_cli/main.py index c1d6d01f89f..cd45ea0b690 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -10332,6 +10332,8 @@ def cmd_dashboard(args): _launch_profile not in ("default", "custom") and not getattr(args, "isolated", False) and not getattr(args, "open_profile", "") + # Desktop pool backends are intentionally per-profile. + and os.environ.get("HERMES_DESKTOP") != "1" ): url = f"http://{args.host or '127.0.0.1'}:{args.port}/?profile={_launch_profile}" if _dashboard_listening(args.host, args.port): diff --git a/tests/hermes_cli/test_dashboard_unified_launch.py b/tests/hermes_cli/test_dashboard_unified_launch.py index 3a14894316d..a02ad659716 100644 --- a/tests/hermes_cli/test_dashboard_unified_launch.py +++ b/tests/hermes_cli/test_dashboard_unified_launch.py @@ -82,6 +82,29 @@ class TestUnifiedDashboardRouting: # Profile HERMES_HOME dropped so the child binds the machine root. assert "HERMES_HOME" not in env + def test_desktop_profile_backend_skips_machine_dashboard_reroute(self, main_mod, monkeypatch): + """A desktop-spawned named-profile backend (HERMES_DESKTOP=1) must NOT + reroute into the machine dashboard. The reroute re-execs as the default + profile and exits, so the desktop never sees a ready backend → boot + loop. The guard keeps desktop pool backends per-profile.""" + monkeypatch.setenv("HERMES_DESKTOP", "1") + monkeypatch.setattr( + "hermes_cli.profiles.get_active_profile_name", lambda: "worker_x" + ) + listening_calls = [] + monkeypatch.setattr( + main_mod, "_dashboard_listening", + lambda host, port: listening_calls.append(1) or False, + ) + execs = [] + monkeypatch.setattr(main_mod.os, "execvpe", lambda *a, **k: execs.append(a)) + monkeypatch.setitem(sys.modules, "fastapi", None) + + with pytest.raises((SystemExit, AttributeError, ImportError, TypeError)): + main_mod.cmd_dashboard(_args()) + assert listening_calls == [] + assert execs == [] + def test_isolated_flag_skips_routing(self, main_mod, monkeypatch): monkeypatch.setattr( "hermes_cli.profiles.get_active_profile_name", lambda: "worker_x"