atlas-iac/testing/tests/test_hermes_cli_capabilities.py
Hermes Agent 7a55b259bf hermes: add the fenced three-node distributed execution pool
Three fenced worker Pods claim Hermes Kanban runs through a coordinator that
owns every state transition, with per-ordinal HMAC authority, a mediated
broker-only SCM path, and durable per-ordinal workspaces.

Content is the reviewed head of PR #18 (689bcb6e) with PR 16's and PR 19's
contributions removed: they were merged in only to validate co-existence and are
not prerequisites, so this branch no longer carries them as ancestors. Only PR 14
and PR 15 remain, because the broker boundary and the cli_lane_* decomposition
are load-bearing for two of the fixed P0 boundaries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 16:31:15 +00:00

277 lines
8.1 KiB
Python

"""Mixed-image capability gates for mounted Hermes CLI lane scripts."""
from __future__ import annotations
import json
import sys
from contextlib import nullcontext
from pathlib import Path
from types import SimpleNamespace
import pytest
from testing.tests.test_hermes_cli_support import _completed_result, lanes
def _old_complete(
_conn,
_task_id,
*,
result=None,
summary=None,
metadata=None,
expected_run_id=None,
):
return bool(result or summary or metadata or expected_run_id)
def _new_complete(
_conn,
_task_id,
*,
result=None,
summary=None,
metadata=None,
expected_run_id=None,
replay_ended_run_id=None,
):
return bool(result or summary or metadata or expected_run_id or replay_ended_run_id)
def _old_reclaim(_conn, _task_id, *, reason, signal_fn=None):
return bool(reason or signal_fn)
def _new_reclaim(
_conn,
_task_id,
*,
reason,
signal_fn=None,
expected_run_id=None,
):
return bool(reason or signal_fn or expected_run_id)
def _db(*, patched: bool):
return SimpleNamespace(
complete_task=_new_complete if patched else _old_complete,
reclaim_task=_new_reclaim if patched else _old_reclaim,
)
def _terminal_document(run_id: int = 7) -> dict:
structured = _completed_result("accepted result")
return {
"board": "cassandra",
"task_id": "t_compat",
"expected_run_id": run_id,
"result": json.dumps(structured, sort_keys=True),
"summary": "accepted result",
"metadata": {},
"kanban_state": "pending",
"recorded_at": lanes.utc_now(),
}
def test_capability_detection_requires_explicit_safety_keywords():
old = lanes.detect_kanban_capabilities(_db(patched=False))
new = lanes.detect_kanban_capabilities(_db(patched=True))
assert old.exact_run_completion is True
assert old.ended_run_replay is False
assert old.exact_run_reclaim is False
assert old.ready is False
assert old.deferred_features == ("ended-run-replay", "exact-run-reclaim")
assert new.ready is True
assert new.deferred_features == ()
variadic = SimpleNamespace(
complete_task=lambda *_args, **_kwargs: True,
reclaim_task=lambda *_args, **_kwargs: True,
)
assert lanes.detect_kanban_capabilities(variadic).ready is False
assert lanes._explicit_keyword(object(), "expected_run_id") is False
missing_completion = lanes.KanbanCapabilities(False, True, True)
assert missing_completion.deferred_features == ("exact-run-completion",)
def test_startup_health_moves_from_bounded_deferred_to_ready(
tmp_path: Path,
capsys,
monkeypatch,
):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
health = tmp_path / "runtime-health.json"
old = lanes.initialize_kanban_capabilities(_db(patched=False), health_path=health)
old_document = lanes.runtime_health(health)
assert old.ready is False
assert old_document["state"] == "deferred"
assert old_document["ready"] is False
assert old_document["active_run_completion_safe"] is True
assert old_document["deferred_features"] == [
"ended-run-replay",
"exact-run-reclaim",
]
assert health.stat().st_size < 2048
assert health.stat().st_mode & 0o777 == 0o600
assert "compatibility deferred" in capsys.readouterr().err
new_db = _db(patched=True)
lanes.kanban_capabilities(new_db)
new = lanes.initialize_kanban_capabilities(new_db, health_path=health)
assert new.ready is True
assert lanes.kanban_capabilities(new_db) is new
assert lanes.runtime_health(health)["state"] == "ready"
@pytest.mark.parametrize(
("status", "run_id", "expected"),
[("running", 7, "committed"), ("blocked", None, "deferred")],
)
def test_old_image_completes_only_the_exact_active_run(
tmp_path: Path,
monkeypatch,
status: str,
run_id: int | None,
expected: str,
):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
task = SimpleNamespace(
id="t_compat",
status=status,
current_run_id=run_id,
completed_run_id=None,
result=None,
assignee="cli-auto",
)
calls = []
def complete(
_conn,
_task_id,
*,
result=None,
summary=None,
metadata=None,
expected_run_id=None,
):
calls.append(expected_run_id)
task.status = "done"
task.current_run_id = None
task.completed_run_id = expected_run_id
task.result = result
return True
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: SimpleNamespace(close=lambda: None),
get_task=lambda _conn, _task_id: task,
complete_task=complete,
reclaim_task=_old_reclaim,
)
identity = lanes.TerminalIdentity("cassandra", "t_compat", 7, "pending")
outcome = lanes._finalize_document_db(db, identity, _terminal_document())
assert outcome == expected
assert calls == ([7] if status == "running" else [])
if expected == "deferred":
assert task.status == "blocked"
def test_old_image_preserves_pending_and_prepared_journals(
tmp_path: Path,
monkeypatch,
):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
task = SimpleNamespace(
id="t_compat",
status="blocked",
current_run_id=None,
completed_run_id=None,
result=None,
assignee="cli-auto",
)
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: SimpleNamespace(close=lambda: None),
get_task=lambda _conn, _task_id: task,
complete_task=_old_complete,
reclaim_task=_old_reclaim,
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
pending, _record = lanes._write_terminal_record(
lanes.state_path("cassandra", "t_compat"),
board="cassandra",
task_id="t_compat",
run_id=7,
structured=_completed_result("accepted result"),
summary="accepted result",
metadata={},
)
assert lanes.recover_pending_finalizations() == 0
assert pending.exists()
assert len(list(pending.parent.glob("*.terminal.prepared-*.json"))) == 1
assert task.status == "blocked"
def test_old_image_never_calls_unguarded_reclaim(tmp_path: Path, monkeypatch):
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "cli-lanes")
task = SimpleNamespace(
id="t_running",
status="running",
current_run_id=11,
assignee="cli-auto",
)
calls = []
def reclaim(_conn, _task_id, *, reason, signal_fn=None):
calls.append((reason, signal_fn))
return True
db = SimpleNamespace(
complete_task=_old_complete,
reclaim_task=reclaim,
list_boards=lambda include_archived=False: [{"slug": "cassandra"}],
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: SimpleNamespace(close=lambda: None),
list_tasks=lambda _conn: [task],
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
monkeypatch.setattr(lanes, "recover_pending_finalizations", lambda: 0)
lanes.recover_orphans()
assert calls == []
def test_main_detects_capabilities_before_startup_recovery(
tmp_path: Path,
monkeypatch,
):
events = []
db = _db(patched=False)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
monkeypatch.setattr(lanes, "RESULT_SCHEMA_PATH", tmp_path / "result.schema.json")
monkeypatch.setattr(
lanes,
"initialize_kanban_capabilities",
lambda candidate: events.append(("detect", candidate))
or lanes.KanbanCapabilities(True, False, False),
)
def stop_after_detection():
events.append(("recover", db))
raise RuntimeError("stop after startup boundary")
monkeypatch.setattr(lanes, "recover_orphans", stop_after_detection)
with pytest.raises(RuntimeError, match="startup boundary"):
lanes.main()
assert events == [("detect", db), ("recover", db)]