Files
hermes-agent/tests/test_hermes_state_ids.py
T
teknium1 c1e58f4cb2 fix(process-identity): desktop reap, Windows updater and profile liveness use the canonical matchers
Two kill/relaunch predicates decided identity by argv substring, the bug class root AGENTS.md
forbids: hermes_cli/dashboard_procs.py::_is_desktop_local_serve_cmdline (`"serve" not in cmd`,
on the orphan-reap KILL path) and hermes_cli/update_cmd_windows.py::_is_backend_argv
(`" serve" in argv_low`, in the very file that defines _hermes_holder_subcommand). Both now ask
the canonical token classifier; host/port are read as flag values, not substrings.

hermes_cli/profiles.py::_check_gateway_running open-coded rungs 1/3 of
gateway.status.resolve_gateway_liveness and skipped the multiplexer rung; it is now that ladder
scoped to the profile dir (pid probe keeps cleanup_stale=False so a probe for another profile
never unlinks its PID file). The gateway/status.py ladder itself is untouched.

Behavior change: `hermes kanban --preserve-cache --host 127.0.0.1 --port 0` and
`-m dashboard serve`-style argv are no longer classified as serve backends (never killed /
relaunched as one); a named profile served by the live default multiplexer now reads as
running from _check_gateway_running (previously only via the separate
_served_by_running_multiplexer OR at some call sites).
2026-09-13 05:21:02 -07:00

49 lines
2.1 KiB
Python

"""Session-id minting contract: every surface that creates a session mints through
``hermes_state_ids.new_session_id`` and the id it produces is what lost-and-found salvage classifies
as a session id (the shape is the recovery sentinel for schema-less rows).
"""
from __future__ import annotations
import importlib
import re
from datetime import datetime
import pytest
import hermes_state_ids
from hermes_state_ids import SESSION_ID_PATTERN, new_session_id
# (module, callable(mint) -> str) for each minting site; ``mint`` is the patched helper.
SITES = [
("hermes_cli.foreign_sessions", None),
("agent.conversation_compression", None),
("hermes_cli.cli_commands_mixin", None),
("hermes_cli.cli_session_mixin", None),
("agent.agent_init", None),
("tui_gateway.server", lambda mod: mod._new_session_key()),
("gateway.session_lifecycle", lambda mod: mod._new_session_id(datetime(2026, 1, 2, 3, 4, 5))),
("hermes_state_portability", None),
("cli", None),
]
@pytest.mark.parametrize("hex_len,expected_re", [(6, r"^\d{8}_\d{6}_[0-9a-f]{6}$"), (8, r"^\d{8}_\d{6}_[0-9a-f]{8}$"),
(12, r"^\d{8}_\d{6}_[0-9a-f]{12}$")])
def test_minted_ids_are_what_salvage_classifies_as_session_ids(hex_len, expected_re):
from hermes_cli.session_lost_and_found import _is_session_id
sid = new_session_id(datetime(2026, 1, 2, 3, 4, 5), hex_len=hex_len)
assert re.fullmatch(expected_re, sid) and sid.startswith("20260102_030405_")
assert _is_session_id(sid)
assert SESSION_ID_PATTERN is importlib.import_module("hermes_cli.session_lost_and_found").SESSION_ID_PATTERN
@pytest.mark.parametrize("module_name,call", SITES)
def test_every_minting_site_imports_the_one_helper(module_name, call):
mod = importlib.import_module(module_name)
minted = [name for name in ("new_session_id", "mint_session_id")
if getattr(mod, name, None) is hermes_state_ids.new_session_id]
assert minted, f"{module_name} does not mint through hermes_state_ids.new_session_id"
if call is not None:
assert SESSION_ID_PATTERN.match(call(mod))