Skip to content

Dashboard and gateway stop treat a multiplexer-served profile as running via the multiplexer (multiplex parity: surfaces) - #108683

Merged
teknium1 merged 1 commit into
mainfrom
fix/mux-parity-surfaces
Sep 12, 2026
Merged

teknium1 merged 1 commit into
mainfrom
fix/mux-parity-surfaces

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

A profile served by the default multiplexer is now reported and managed as running via the multiplexer by the dashboard/Desktop API and by hermes -p X gateway stop, matching what hermes -p X status / gateway status / cron status already said.

Symptom → change → behaviour

Live rig: temp HOME, default multiplexer (api_server on loopback) serving alpha + beta; compared against a standalone hermes -p alpha gateway run.

Surface Standalone alpha Served alpha — before Served alpha — after
GET /api/status?profile=alpha gateway_running: true, pid, state: running, platforms gateway_running: false, pid: null, state: null — in the same body whose gateways[].served_profiles lists alpha gateway_running: true, pid: <multiplexer>, state: running, alpha's <alpha>:<platform> entries projected as its platforms
GET /api/messaging/platforms?profile=alpha gateway_running: true gateway_running: false, state: gateway_stopped → Channels page says "The gateway is not running" gateway_running: true
POST /api/gateway/stop?profile=alpha stops alpha spawns hermes -p alpha gateway stop → "No gateway running for this profile" exit 0 in the action log; UI flips to stopped, multiplexer keeps serving alpha 409 with the multiplexer explanation (same helper the existing start refusal now uses)
POST /api/gateway/restart?profile=alpha (also the post-onboarding restart for Telegram/WhatsApp) restarts alpha spawns hermes -p alpha gateway restart → exit 78 into the action log, nothing restarted restarts the multiplexer — the process that actually serves alpha (verified: new multiplexer pid, served_profiles intact)
hermes -p alpha gateway stop "✓ Stopped gateway for this profile" "✗ No gateway running for this profile" exit 0 — contradicts gateway status on the same profile exit 78 refusal pointing at hermes gateway stop/restart on the default profile, like run/start/install/restart

Root cause: a served profile owns no gateway.pid / gateway_state.json (#97120), so every reader of per-profile identity files called it stopped; only the CLI status paths had been taught about served_profiles.

  • gateway/status.py::resolve_gateway_liveness — 4th rung, scoped requests only: the live default multiplexer whose record lists the profile in served_profiles is that profile's gateway (source="multiplexer", runtime = its record). New multiplexer_liveness_for_profile / profile_platforms_from_multiplexer helpers; GatewayLiveness.runtime field.
  • hermes_cli/web_routers/status.py, web_routers/messaging.py — consume the multiplexer runtime, re-keyed to the standalone platform shape.
  • hermes_cli/web_server_gateway.py::_gateway_subcommand — restart for a served profile drops -p X; multiplexed_profile_refusal(profile, verb) shared by /api/gateway/start and /stop (web_routers/ops.py). A --force-started separate gateway for X (own pid file) keeps normal per-profile management.
  • hermes_cli/gateway.py::_cmd_stop — exit-78 refusal when the profile is served and has no gateway of its own; --all unaffected.
  • Docs: website/docs/user-guide/multi-profile-gateways.md §1 and §5.

Verified unchanged/already at parity in the same rig (no code change): hermes -p X status|gateway status|gateway list|cron status|cron list|cron run|profile list|profile show|logs|sessions list, run/start/install/restart exit 78, hermes update --plan lists ONE gateway [default] (fleet restart hits the multiplexer once), gateway_state.json shape (served_profiles + <profile>:<platform> only under the default home), per-profile agent.log/errors.log/gateway.log under profiles/X/logs for X's turns, Desktop ticker standdown (#108428) and shutdown//update notices via X's adapter (#107655) on fresh main.

Tests

tests/hermes_cli/test_gateway_multiplex_served_record.py +3 invariants (ladder reports served profile running with its platforms and keeps an unserved one stopped; lifecycle verbs target the multiplexer unless the profile runs its own gateway; CLI stop refuses) — red on base (3 failed), green here. tests/hermes_cli/ + tests/gateway/: 17610 passed; 2 failures are pre-existing/unrelated (test_update_head_moved_gate::test_update_success_when_head_moves fails on base too; test_session_hygiene::test_hygiene_does_not_wait_ceiling_after_fence_cancel is a load-timing flake, passes in isolation on this tree).

Compatibility with #106742

Does not touch hermes_cli/web_server.py, web_server_lifecycle.py, subcommands/gateway.py, apps/desktop/electron/main.ts, apps/desktop/src/store/profile.ts, or tui_gateway/methods_tools.py. The restart routing is placed in web_server_gateway.py::_gateway_subcommand, which web_server.py::_spawn_gateway_restart already calls, so #106742's rewrite of that file inherits it without a hunk.

Not done

  • fix(dashboard): isolate the environment of named-profile actions #107434 (dashboard hermes -p X action-child env) — owned by another lane.
  • hermes gateway status on a manually-run default profile lists unrelated gateway PIDs from other HERMES_HOMEs (_scan_gateway_pids default-profile branch accepts any gateway run without -p/HERMES_HOME= in argv). Pre-existing and identical in standalone; not a parity bug.

Infographic

served-profile parity

…ateway as the multiplexer

A profile served by the default multiplexer owns no gateway.pid / gateway_state.json,
so every surface that reads per-profile identity files called it stopped while the CLI
status surfaces (hermes -p X status / gateway status / cron status) said "running via the
default-profile multiplexer":

- `/api/status?profile=X` and `/api/messaging/platforms?profile=X` reported
  gateway_running=false / state=None / "gateway_stopped" in the same body that listed X
  under gateways[].served_profiles. The shared ladder `resolve_gateway_liveness` gains a
  fourth rung for a named profile_dir: the live default multiplexer that records X in
  served_profiles IS X's gateway (pid = multiplexer pid, runtime = its record, X's
  `<X>:<platform>` entries re-keyed to the standalone shape).
- `POST /api/gateway/stop?profile=X` spawned `hermes -p X gateway stop`, which printed
  "No gateway running for this profile" (exit 0) into the action log while the UI flipped
  to stopped and the multiplexer kept serving X; `/api/gateway/restart?profile=X` spawned
  a `-p X gateway restart` that only exits 78. start/stop now answer 409 with the
  multiplexer explanation (one helper shared with the existing start refusal) and restart
  targets the multiplexer, the process that actually serves X. A `--force`-started
  separate gateway for X (own pid file) keeps normal per-profile management.
- CLI `hermes -p X gateway stop` refuses with exit 78 like run/start/install/restart when
  X has no gateway of its own, instead of a contradictory exit-0 "not running".

Docs: multi-profile-gateways.md §1 and §5 describe stop + the dashboard behaviour.
@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown

૮ >ﻌ< ა ci review

ran on 99f49e6 — fix(multiplex): dashboard and gateway stop see a served pr

⚠️ Warnings

OSV vulnerability scan · View job

80 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 4m39s vs 4m45s (-2.1%). 3 job(s) slower, 9 faster, 3 unchanged.

  • Python tests / Run tests: -43.0s
  • Docs Site / docs-site-checks: -36.0s
  • Check no case-colliding filenames / check-case-collisions: -34.0s
  • OS-specific tests / macOS-only tests: +16.0s
  • Profile artifact check / Reject profile archives: -6.0s

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) area/profiles Multi-profile isolation, HERMES_HOME scoping labels Sep 12, 2026
@teknium1
teknium1 merged commit 446f6f7 into main Sep 12, 2026
39 checks passed
@teknium1
teknium1 deleted the fix/mux-parity-surfaces branch September 12, 2026 02:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants