fix: drive cold-start multiplex target from the gateway boot policy

gateway_runtime_multiplex picked the cold-start owner for a named profile
with its own reading of gateway.multiplex_profiles: a retired explicit
`false` was treated as an opt-out (spawning a per-profile owner the boot
would refuse/ignore), and an explicit `true` selected the root even for a
`gateway.standalone: true` secondary that profiles_to_serve never serves,
so the client waited on a multiplexer that would not serve it.

Export the boot verdict from gateway_multiplex_mode
(settled_explicit_flag, used by resolve_multiplex_mode, and
host_serves_named_profile) and have implied_host_root /
multiplexer_serves_home call it: retired false settles like unset (fall
back to the recorded served set, else the host root) and a standalone
secondary always gets its own owner.

Finding: R1/R2 minor "cold-start multiplex target selection vs policy".
(cherry picked from commit e5144060d8a49ef77ba4de5149279c1942dbe235)
This commit is contained in:
Teknium committed 2026-10-06 14:24:39 -07:00
1 parent 8924db5647
commit a257be0ba7
3 files changed
+57 -20

No files matched your search

+18 -1
View File
@@ -76,6 +76,23 @@ def explicit_multiplex_flag(default_home: Path) -> Optional[bool]:
return bool(value)
def settled_explicit_flag(value: Optional[bool]) -> Optional[bool]:
"""How boot reads an explicit ``multiplex_profiles``: ``true`` is final; the retired ``false``
settles exactly like an unset key (``None``: the boot preflight decides)."""
return True if value else None
def host_serves_named_profile(default_home: Path, home: Path) -> Optional[bool]:
"""Offline twin of the boot verdict for named profile *home* under *default_home*'s gateway:
``False`` for a ``gateway.standalone: true`` secondary (``profiles_to_serve`` never serves it),
``True`` for an explicit ``true`` on the default, ``None`` when boot settles it (unset or the
retired ``false``, see :func:`resolve_multiplex_mode`)."""
from hermes_cli.profiles import profile_is_standalone
if profile_is_standalone(home):
return False
return settled_explicit_flag(explicit_multiplex_flag(default_home))
def default_gateway_multiplexes(default_home: Optional[Path] = None) -> bool:
"""Does the default profile's gateway serve every profile? For CLI/dashboard processes: the LIVE
gateway's ``served_profiles`` record when one runs (it settled the unset default itself), else
@@ -240,7 +257,7 @@ def resolve_multiplex_mode(config) -> MultiplexDecision:
standalone = standalone_launcher_decision(config)
if standalone is not None:
return standalone
if current:
if settled_explicit_flag(current):
return MultiplexDecision(True, "config")
retired_opt_out = current is False
try:
+16 -19
View File
@@ -5,9 +5,10 @@ When the multiplexer is down (restart, update, crash) the profile looks ``absent
loop would spawn a per-profile gateway for it — a second owner that answers this client and then
blocks the multiplexer's next all-or-nothing reserve (``Cannot reserve gateway profiles``). The
client must start the multiplexer instead, so the start target is decided here from the same facts
the multiplexer boots on: the default profile's explicit ``gateway.multiplex_profiles`` flag, or —
when the flag is unset and settled at boot — the served set the last multiplexer recorded in the
root's ``gateway_state.json`` (a standalone default gateway clears it).
the multiplexer boots on (``gateway_multiplex_mode.host_serves_named_profile``): a profile's own
``gateway.standalone: true``, the default profile's explicit ``true``, or — when the flag is unset or
the retired ``false`` that boot settles like unset — the served set the last multiplexer recorded in
the root's ``gateway_state.json`` (a standalone default gateway clears it).
"""
from __future__ import annotations
@@ -27,36 +28,32 @@ def implied_host_root(home: Path) -> Path | None:
"""The root whose gateway must serve named profile *home* when nothing records a verdict.
``hermes gateway run`` refuses a ``profiles/<name>`` home a gateway of its own unless it authored
``gateway.standalone: true`` (or passes ``--force``); only an explicit multiplex ``false`` on the
root leaves the profile to its own daemon.
``gateway.standalone: true`` (or passes ``--force``); the retired ``multiplex_profiles: false``
no longer opts out, so only a standalone profile keeps its own daemon.
"""
root = multiplexer_root_for(home)
if root is None:
return None
from hermes_cli.gateway_multiplex_mode import explicit_multiplex_flag
from hermes_cli.profiles import profile_is_standalone
if explicit_multiplex_flag(root) is False or profile_is_standalone(home):
return None
return root
from hermes_cli.gateway_multiplex_mode import host_serves_named_profile
return None if host_serves_named_profile(root, home) is False else root
def multiplexer_serves_home(home: Path) -> Path | None:
"""The root whose (possibly stopped) multiplexer serves *home*, else None.
``hermes_cli.gateway.named_profile_served_by_running_multiplexer`` answers the live question;
this is its offline twin for the moment no gateway runs: an explicit ``true`` on the default
profile, or a recorded ``served_profiles`` naming this profile (the boot-time verdict of an unset
flag). An explicit ``false``, or no evidence, keeps the per-profile daemon.
this is its offline twin for the moment no gateway runs, deciding from the boot policy
(:func:`hermes_cli.gateway_multiplex_mode.host_serves_named_profile`): a standalone secondary is
never served, an explicit ``true`` serves the rest, and an unset or retired ``false`` flag falls
back to the ``served_profiles`` the last multiplexer recorded. No evidence keeps it unanswered.
"""
root = multiplexer_root_for(home)
if root is None:
return None
from hermes_cli.gateway_multiplex_mode import explicit_multiplex_flag
flag = explicit_multiplex_flag(root)
if flag is True:
return root
if flag is False:
return None
from hermes_cli.gateway_multiplex_mode import host_serves_named_profile
verdict = host_serves_named_profile(root, home)
if verdict is not None:
return root if verdict else None
from gateway.status import read_runtime_status
from hermes_cli.profiles import normalize_profile_name
served = (read_runtime_status(root / "gateway_state.json") or {}).get("served_profiles")
@@ -332,3 +332,26 @@ def test_named_profile_without_multiplex_evidence_starts_the_host_gateway(tmp_pa
assert spawned == [(home if standalone else root).resolve()]
@pytest.mark.parametrize("flag,standalone,owner", [("false", False, "root"), ("true", True, "home")])
def test_cold_start_target_follows_boot_multiplex_policy(tmp_path, monkeypatch, flag, standalone, owner):
"""Boot settles a retired `multiplex_profiles: false` like unset (the host still serves the
profile) and never serves a `standalone: true` secondary even under an explicit `true`."""
from hermes_cli import gateway_runtime as runtime, gateway_runtime_service as service, gateway_runtime_start as start
monkeypatch.delenv("GATEWAY_MULTIPLEX_PROFILES", raising=False)
root = tmp_path / ".hermes"
home = root / "profiles" / "alpha"
home.mkdir(parents=True, mode=0o700)
(root / "config.yaml").write_text(f"gateway:\n multiplex_profiles: {flag}\n", encoding="utf-8")
if standalone:
(home / "config.yaml").write_text("gateway:\n standalone: true\n", encoding="utf-8")
spawned = []
monkeypatch.setattr(runtime, "discover_gateway_endpoint", lambda *a, **k: runtime.GatewayDiscovery("absent"))
monkeypatch.setattr(service, "discover_existing_gateway_service", lambda *a, **k: None)
monkeypatch.setattr(start, "spawn_unmanaged_gateway", lambda target, **k: spawned.append(Path(target)))
runtime.ensure_gateway_runtime(home, timeout=0.3)
assert spawned == [{"root": root, "home": home}[owner].resolve()]