atlas-iac/testing/tests/test_hermes_cli_execution_edges.py
jenkins df75479069 hermes: close lane branch coverage gaps
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 15:03:59 -03:00

320 lines
12 KiB
Python

"""Execution-loop edge coverage for preparation, callbacks, and recovery."""
from __future__ import annotations
import sqlite3
import sys
from contextlib import nullcontext
from pathlib import Path
from types import SimpleNamespace
from testing.tests.test_hermes_cli_support import _completed_result, lanes
class _Connection:
def close(self):
return None
def test_missing_claim_is_a_noop(tmp_path: Path, monkeypatch):
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: _Connection(),
get_task=lambda *_args: None,
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
lanes.execute_claim("cassandra", "missing")
def test_transient_callback_storage_errors_do_not_kill_provider(
tmp_path: Path,
monkeypatch,
):
task = SimpleNamespace(
id="t_callbacks",
status="running",
current_run_id=20,
assignee="cli-auto",
max_runtime_seconds=60,
)
blocks = []
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: _Connection(),
get_task=lambda *_args: task,
worker_log_path=lambda *_args, **_kwargs: tmp_path / "worker.log",
_resolve_worktree_workspace=lambda *_args, **_kwargs: (tmp_path, "branch"),
set_branch_name=lambda *_args: None,
set_workspace_path=lambda *_args: None,
build_worker_context=lambda *_args: "exercise callbacks",
heartbeat_worker=lambda *_args, **_kwargs: (_ for _ in ()).throw(
sqlite3.OperationalError("heartbeat volume")
),
add_comment=lambda *_args, **_kwargs: (_ for _ in ()).throw(
sqlite3.OperationalError("comment volume")
),
block_task=lambda *_args, **kwargs: blocks.append(kwargs),
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "lanes")
monkeypatch.setattr(lanes, "KANBAN_STORAGE_ATTEMPTS", 1)
monkeypatch.setattr(lanes, "fresh_unavailable_provider", lambda: "claude")
route = lanes.Route("codex", "gpt", "high", "p", "c", "r", 1, ())
monkeypatch.setattr(lanes, "select_route", lambda *_args, **_kwargs: route)
def provider(*args, **_kwargs):
assert args[6]("still working") is True
return lanes.ProcessResult(1, "failed", None, False)
monkeypatch.setattr(lanes, "run_provider", provider)
lanes.execute_claim("cassandra", "t_callbacks")
assert blocks and blocks[0]["kind"] == "capability"
def test_restart_handoff_survives_missing_prior_log(tmp_path: Path, monkeypatch):
task = SimpleNamespace(
id="t_restart",
status="running",
current_run_id=21,
assignee="cli-auto",
max_runtime_seconds=60,
)
state_root = tmp_path / "lanes"
monkeypatch.setattr(lanes, "STATE_ROOT", state_root)
state_file = lanes.state_path("cassandra", "t_restart")
lanes.atomic_json(state_file, {"current_route": {"provider": "claude"}})
blocks = []
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: _Connection(),
get_task=lambda *_args: task,
worker_log_path=lambda *_args, **_kwargs: tmp_path / "missing.log",
_resolve_worktree_workspace=lambda *_args, **_kwargs: (tmp_path, "branch"),
set_branch_name=lambda *_args: None,
set_workspace_path=lambda *_args: None,
build_worker_context=lambda *_args: "resume",
add_comment=lambda *_args, **_kwargs: None,
heartbeat_worker=lambda *_args, **_kwargs: True,
block_task=lambda *_args, **kwargs: blocks.append(kwargs),
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
route = lanes.Route("codex", "gpt", "high", "p", "c", "r", 1, ())
monkeypatch.setattr(lanes, "select_route", lambda *_args, **_kwargs: route)
handoffs = []
monkeypatch.setattr(
lanes,
"git_handoff",
lambda _workspace, output: handoffs.append(output) or "handoff",
)
monkeypatch.setattr(
lanes,
"run_provider",
lambda *_args, **_kwargs: lanes.ProcessResult(1, "failed", None, False),
)
lanes.execute_claim("cassandra", "t_restart")
assert handoffs == ["Previous provider log was unavailable after restart."]
assert blocks
def test_unreplayable_persistence_error_recovers_exact_run(
tmp_path: Path,
monkeypatch,
):
task = SimpleNamespace(
id="t_persist",
status="running",
current_run_id=22,
assignee="cli-auto",
max_runtime_seconds=60,
)
comments = []
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: _Connection(),
get_task=lambda *_args: task,
worker_log_path=lambda *_args, **_kwargs: tmp_path / "worker.log",
_resolve_worktree_workspace=lambda *_args, **_kwargs: (tmp_path, "branch"),
set_branch_name=lambda *_args: None,
set_workspace_path=lambda *_args: None,
build_worker_context=lambda *_args: "finish",
add_comment=lambda _conn, _task, _author, body: comments.append(body),
heartbeat_worker=lambda *_args, **_kwargs: True,
block_task=lambda *_args, **_kwargs: None,
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "lanes")
monkeypatch.setattr(
lanes,
"select_route",
lambda *_args, **_kwargs: lanes.Route(
"codex", "gpt", "high", "p", "c", "r", 1, ()
),
)
monkeypatch.setattr(
lanes,
"run_provider",
lambda *_args, **_kwargs: lanes.ProcessResult(
0, "", _completed_result("accepted"), False
),
)
monkeypatch.setattr(
lanes,
"_write_terminal_record",
lambda *_args, **_kwargs: (_ for _ in ()).throw(OSError("disk")),
)
monkeypatch.setattr(lanes, "_has_pending_finalization", lambda *_args: False)
recovered = []
monkeypatch.setattr(
lanes,
"_recover_exact_run",
lambda *args: recovered.append(args) or True,
)
lanes.execute_claim("cassandra", "t_persist")
assert recovered and recovered[0][1].run_id == 22
assert any("terminal replayable=False" in body for body in comments)
def test_unexpected_route_exception_blocks_the_exact_run(
tmp_path: Path,
monkeypatch,
):
task = SimpleNamespace(
id="t_route",
status="running",
current_run_id=23,
assignee="cli-auto",
max_runtime_seconds=60,
)
blocks = []
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: _Connection(),
get_task=lambda *_args: task,
worker_log_path=lambda *_args, **_kwargs: tmp_path / "worker.log",
_resolve_worktree_workspace=lambda *_args, **_kwargs: (tmp_path, "branch"),
set_branch_name=lambda *_args: None,
set_workspace_path=lambda *_args: None,
build_worker_context=lambda *_args: "route",
add_comment=lambda *_args, **_kwargs: None,
heartbeat_worker=lambda *_args, **_kwargs: True,
block_task=lambda *_args, **kwargs: blocks.append(kwargs),
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "lanes")
monkeypatch.setattr(
lanes,
"select_route",
lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("router down")),
)
lanes.execute_claim("cassandra", "t_route")
assert blocks[0]["expected_run_id"] == 23
assert "router down" in blocks[0]["reason"]
def test_capacity_fallback_without_evidence_blocks_with_provider_output(
tmp_path: Path,
monkeypatch,
):
"""A fallback turn with no structured result blocks on raw provider output."""
task = SimpleNamespace(
id="t_nofallback",
status="running",
current_run_id=22,
assignee="cli-auto",
max_runtime_seconds=60,
)
blocks = []
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: _Connection(),
get_task=lambda *_args: task,
worker_log_path=lambda *_args, **_kwargs: tmp_path / "worker.log",
_resolve_worktree_workspace=lambda *_args, **_kwargs: (tmp_path, "branch"),
set_branch_name=lambda *_args: None,
set_workspace_path=lambda *_args: None,
build_worker_context=lambda *_args: "attempt both providers",
add_comment=lambda *_args, **_kwargs: None,
heartbeat_worker=lambda *_args, **_kwargs: True,
block_task=lambda *_args, **kwargs: blocks.append(kwargs),
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "lanes")
monkeypatch.setattr(lanes, "fresh_unavailable_provider", lambda: None)
codex = lanes.Route("codex", "gpt", "high", "p", "c", "r", 1, ())
claude = lanes.Route("claude", "fable", "high", "p", "c", "r", 1, ())
monkeypatch.setattr(
lanes,
"select_route",
lambda _prompt, assignee, **_kwargs: claude
if assignee == "cli-claude-high"
else codex,
)
monkeypatch.setattr(lanes, "git_handoff", lambda *_args: "handoff")
reports = [
lanes.ProcessResult(1, "usage limit reached", None, True),
lanes.ProcessResult(1, "fallback also failed", None, False),
]
monkeypatch.setattr(lanes, "run_provider", lambda *_args, **_kwargs: reports.pop(0))
lanes.execute_claim("cassandra", "t_nofallback")
assert reports == []
assert blocks and "fallback also failed" in blocks[0]["reason"]
assert blocks[0]["kind"] == "capability"
def test_worker_blockers_become_the_block_reason_without_goal_mode(
tmp_path: Path,
monkeypatch,
):
"""A structured non-completed report blocks with its own stated blockers."""
task = SimpleNamespace(
id="t_blocked",
status="running",
current_run_id=23,
assignee="cli-auto",
max_runtime_seconds=60,
)
blocks = []
db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: _Connection(),
get_task=lambda *_args: task,
worker_log_path=lambda *_args, **_kwargs: tmp_path / "worker.log",
_resolve_worktree_workspace=lambda *_args, **_kwargs: (tmp_path, "branch"),
set_branch_name=lambda *_args: None,
set_workspace_path=lambda *_args: None,
build_worker_context=lambda *_args: "report the obstacle",
add_comment=lambda *_args, **_kwargs: None,
heartbeat_worker=lambda *_args, **_kwargs: True,
block_task=lambda *_args, **kwargs: blocks.append(kwargs),
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=db))
monkeypatch.setattr(lanes, "STATE_ROOT", tmp_path / "lanes")
monkeypatch.setattr(lanes, "fresh_unavailable_provider", lambda: None)
route = lanes.Route("codex", "gpt", "high", "p", "c", "r", 1, ())
monkeypatch.setattr(lanes, "select_route", lambda *_args, **_kwargs: route)
structured = {
**_completed_result("progress only"),
"status": "blocked",
"blockers": ["credentials expired", "remote unreachable"],
}
monkeypatch.setattr(
lanes,
"run_provider",
lambda *_args, **_kwargs: lanes.ProcessResult(0, "out", structured, False),
)
lanes.execute_claim("cassandra", "t_blocked")
assert blocks and blocks[0]["reason"] == (
"credentials expired; remote unreachable"
)