atlas-iac/testing/tests/test_hermes_handoff_acceptance.py
Hermes Agent 8f00545828 hermes: add a fail-closed full-handoff acceptance harness
Decides whether the Hermes platform handoff is fit to release, and refuses
to round an absence of evidence up to a pass.

The harness is read-only by default and classifies 71 checks PASS / FAIL /
NOT_RUN / NOT_APPLICABLE. Any mandatory FAIL or NOT_RUN is NO_GO, and so is a
harness-level problem: an unreachable vantage, a catalog entry whose evidence
no longer exists, an expired deadline, or an evaluator that raised.

Evidence comes from two vantages that cannot cover for each other: an external
read-only operator kubeconfig, and the Hermes agent probing itself from inside
its own pod. Before any check runs, the harness asks each vantage who it is and
stops if they are the same principal, because dual-vantage evidence from one
identity is a restatement rather than a corroboration. `--as` is rejected for
every operator-side command and reachable only as the inner command of a
`kubectl exec`, so impersonation can never stand in for a real self-probe. A
deny check needs a live refused request, not only an authorization review.

Two safety properties are structural rather than conventional, enforced where
an argv becomes a subprocess: the default mode mutates nothing (mutating verbs
require a server dry run; there is deliberately no live TokenRequest probe,
because a successful one would mint a real credential), and no probe can pull a
credential value into a report (no vault/sops/curl, secrets readable only with
-o name, environment probes list names, shell only through frozen reviewed
templates). Captures are bounded before they are screened, and the rendered
report is re-screened before it is written.

Mutation lives behind a separate arming flag with an exact confirmation phrase,
a caller-supplied unique ref, a preflight that refuses a protected push target
before any network call, and a cleanup whose verification is itself mandatory.
A default run reports those four checks NOT_RUN.

The catalog is declarative so a reviewer reads what is asserted rather than how
it is plumbed, and so structural properties can be proven over every entry
before a run. Catalog drift surfaces as NOT_RUN, which stops the release.

docs/hermes_full_handoff_acceptance.md carries the merge order for PRs #14-#18
on top of the merged #13 baseline, the image build and Flux rollout, the
rollback point for each step, the go/no-go checklist, and the limits that are
asserted rather than exercised.

Validation: 295 handoff tests pass with 100% line coverage on all 15 new
modules; the full unit suite is 647 passed with two failures that reproduce
unchanged on origin/main; Ruff, py_compile, kustomize render, and a diff
credential screen are clean; a live read-only run against Atlas returns NO_GO
for the pre-merge cluster with no unscreened fields in the report.
2026-08-17 10:14:17 +00:00

396 lines
14 KiB
Python

"""End-to-end contracts for the Hermes full-handoff acceptance harness CLI."""
from __future__ import annotations
import json
import subprocess
import pytest
from testing.tests.test_hermes_handoff_support import FakeClock, load_handoff_module
acceptance = load_handoff_module("hermes_handoff_acceptance")
catalog = load_handoff_module("hermes_handoff_catalog")
exec_module = load_handoff_module("hermes_handoff_exec")
model = load_handoff_module("hermes_handoff_model")
policy = load_handoff_module("hermes_handoff_policy")
harness_run = load_handoff_module("hermes_handoff_run")
def completed(stdout: str = "", stderr: str = "", returncode: int = 0):
return subprocess.CompletedProcess(args=[], returncode=returncode, stdout=stdout, stderr=stderr)
class ScriptedCluster:
"""A ``subprocess.run`` stand-in that answers by command shape.
Real runs issue a couple of hundred commands; scripting them positionally
would make every test a brittle ordering puzzle. Matching on the command
line keeps each test about the behaviour it is checking.
"""
def __init__(self, rules: list[tuple[str, object]], default=None) -> None:
self.rules = rules
self.default = default or completed(stderr="not found", returncode=1)
self.calls: list[list[str]] = []
def __call__(self, argv, **_kwargs):
self.calls.append(list(argv))
line = " ".join(argv)
for fragment, response in self.rules:
if fragment in line:
return response
return self.default
def whoami(username: str):
return completed(stdout=json.dumps({"status": {"userInfo": {"username": username}}}))
HEALTHY_CLUSTER = [
("auth whoami", whoami("kubernetes-admin")),
("get pods --selector app=hermes-agent", completed(stdout="pod/hermes-agent-1\n")),
("get pods --selector", completed(stdout="pod/other-1\n")),
("get pod/hermes-chat-tenant-0", completed(stdout="pod/hermes-chat-tenant-0\n")),
]
def run_cli(monkeypatch, argv, rules):
cluster = ScriptedCluster(rules)
clock = FakeClock()
def build_runner(**kwargs):
return exec_module.Runner(clock=clock, spawn=cluster, environ={}, **kwargs)
monkeypatch.setattr(acceptance, "Runner", build_runner)
return (acceptance.main(argv), cluster)
def read_report(path):
return json.loads(path.read_text(encoding="utf-8"))
def test_the_parser_exposes_the_documented_controls() -> None:
arguments = acceptance.build_parser().parse_args(
[
"--namespace",
"hermes",
"--context",
"atlas-operator",
"--dependency-pr",
"14",
"--dependency-pr",
"15",
"--expected-suspension",
"flux-system/parked",
"--node-count",
"21",
"--pool-worker-env",
"HERMES_POOL_LEASE_SECONDS",
"--expect-telegram-sessions",
]
)
targets = acceptance.targets_from_args(arguments, None)
assert targets.dependency_pull_requests == (14, 15)
assert targets.expected_suspensions == ("flux-system/parked",)
assert targets.node_count == 21
assert targets.pool_worker_env == ("HERMES_POOL_LEASE_SECONDS",)
assert targets.expect_telegram_sessions is True
def test_defaults_fall_back_to_the_shipped_targets() -> None:
arguments = acceptance.build_parser().parse_args([])
targets = acceptance.targets_from_args(arguments, None)
assert targets.dependency_pull_requests == catalog.Targets.dependency_pull_requests
assert targets.expected_suspensions == ()
assert targets.repo == catalog.Targets.repo
def test_a_default_run_is_read_only_and_never_pushes_or_posts(monkeypatch, tmp_path) -> None:
output = tmp_path / "report.json"
status, cluster = run_cli(
monkeypatch, ["--output", str(output), "--json-only"], HEALTHY_CLUSTER
)
report = read_report(output)
assert status == acceptance.EXIT_NO_GO
assert report["mode"] == policy.READ_ONLY
assert not any("push" in call for call in cluster.calls)
assert not any("POST" in call for call in cluster.calls)
assert not any("DELETE" in call for call in cluster.calls)
writes = [
call
for call in cluster.calls
if {"patch", "create", "delete", "apply", "replace"} & set(call) and "can-i" not in call
]
assert writes
assert all("--dry-run=server" in call for call in writes)
def test_mutating_checks_report_not_run_until_the_mode_is_armed(monkeypatch, tmp_path) -> None:
output = tmp_path / "report.json"
run_cli(monkeypatch, ["--output", str(output), "--json-only"], HEALTHY_CLUSTER)
ephemeral = {
check["id"]: check
for check in read_report(output)["checks"]
if check["group"] == "ephemeral"
}
assert len(ephemeral) == 4
for check in ephemeral.values():
assert check["status"] == model.NOT_RUN
assert check["mandatory"] is False
assert check["scope"] == model.EPHEMERAL
def test_a_zero_state_cluster_is_no_go_with_every_check_accounted_for(
monkeypatch, tmp_path
) -> None:
"""Nothing observed must never read as nothing wrong."""
output = tmp_path / "report.json"
status, _ = run_cli(
monkeypatch,
["--output", str(output), "--json-only"],
[("auth whoami", whoami("kubernetes-admin"))],
)
report = read_report(output)
assert status == acceptance.EXIT_NO_GO
assert report["decision"] == model.NO_GO
assert report["counts"][model.PASS] == 0
assert sum(report["counts"].values()) == len(report["checks"])
assert any("in-pod Hermes vantage is unavailable" in error for error in report["harness_errors"])
def test_partial_failure_is_reported_per_check_rather_than_aborting(
monkeypatch, tmp_path
) -> None:
output = tmp_path / "report.json"
rules = [
*HEALTHY_CLUSTER,
("auth can-i", completed(stdout="no\n", returncode=1)),
("get namespaces", completed(stdout="namespace/hermes\n")),
("merge-base", completed()),
]
status, _ = run_cli(monkeypatch, ["--output", str(output), "--json-only"], rules)
report = read_report(output)
statuses = {check["status"] for check in report["checks"]}
assert status == acceptance.EXIT_NO_GO
assert model.PASS in statuses and model.NOT_RUN in statuses
assert report["blocking"]
def test_a_vantage_collision_is_a_no_go_even_with_clean_checks(monkeypatch, tmp_path) -> None:
output = tmp_path / "report.json"
rules = [
("exec --namespace hermes hermes-agent-1", whoami("system:serviceaccount:hermes:hermes-agent")),
("auth whoami", whoami("system:serviceaccount:hermes:hermes-agent")),
("get pods --selector app=hermes-agent", completed(stdout="pod/hermes-agent-1\n")),
("get pods --selector", completed(stdout="pod/other-1\n")),
("get pod/hermes-chat-tenant-0", completed(stdout="pod/hermes-chat-tenant-0\n")),
]
status, _ = run_cli(monkeypatch, ["--output", str(output), "--json-only"], rules)
report = read_report(output)
assert status == acceptance.EXIT_NO_GO
assert any("same principal" in error for error in report["harness_errors"])
def test_an_exhausted_deadline_leaves_the_remaining_checks_not_run(monkeypatch, tmp_path) -> None:
output = tmp_path / "report.json"
status, _ = run_cli(
monkeypatch, ["--output", str(output), "--json-only", "--deadline", "0"], HEALTHY_CLUSTER
)
report = read_report(output)
assert status == acceptance.EXIT_NO_GO
assert report["counts"][model.NOT_RUN] > 0
assert any("deadline expired" in error for error in report["harness_errors"])
def test_arming_with_a_wrong_confirmation_stops_before_anything_runs(
monkeypatch, tmp_path
) -> None:
output = tmp_path / "report.json"
status, cluster = run_cli(
monkeypatch,
[
"--output",
str(output),
"--json-only",
"--arm-ephemeral-push",
"--confirm",
"please",
"--ephemeral-token",
"acceptance-20260817a",
],
HEALTHY_CLUSTER,
)
report = read_report(output)
assert status == acceptance.EXIT_NO_GO
assert "arming refused" in report["harness_errors"][0]
assert not any("push" in call for call in cluster.calls)
def test_arming_against_a_protected_push_target_is_refused(monkeypatch, tmp_path) -> None:
output = tmp_path / "report.json"
status, cluster = run_cli(
monkeypatch,
[
"--output",
str(output),
"--json-only",
"--arm-ephemeral-push",
"--confirm",
acceptance.CONFIRMATION,
"--ephemeral-token",
"main",
],
HEALTHY_CLUSTER,
)
assert status == acceptance.EXIT_NO_GO
assert "arming refused" in read_report(output)["harness_errors"][0]
assert not any("push" in call for call in cluster.calls)
def test_an_armed_run_pushes_one_ephemeral_ref_and_verifies_its_removal(
monkeypatch, tmp_path
) -> None:
output = tmp_path / "report.json"
rules = [
("git ls-remote origin refs/heads/ephemeral", completed(stdout="")),
("git push", completed()),
("POST /api/v1/repos/atlas/titan-iac/pulls", completed(stdout=json.dumps({"number": 9}))),
("PATCH", completed()),
("DELETE", completed()),
("GET /api/v1/repos/atlas/titan-iac/pulls/9", completed(stdout='{"state": "closed"}')),
*HEALTHY_CLUSTER,
]
status, cluster = run_cli(
monkeypatch,
[
"--output",
str(output),
"--json-only",
"--arm-ephemeral-push",
"--confirm",
acceptance.CONFIRMATION,
"--ephemeral-token",
"acceptance-20260817a",
],
rules,
)
report = read_report(output)
ephemeral = {check["id"]: check for check in report["checks"] if check["group"] == "ephemeral"}
assert report["mode"] == policy.ARMED
assert status == acceptance.EXIT_NO_GO # the rest of the cluster is still unproven
assert ephemeral["ephemeral.feature-branch-push"]["status"] == model.PASS
assert ephemeral["ephemeral.draft-pull-request"]["status"] == model.PASS
assert ephemeral["ephemeral.cleanup-verified"]["status"] == model.PASS
assert ephemeral["ephemeral.protected-branch-refusal"]["status"] == model.PASS
pushes = [call for call in cluster.calls if "push" in call]
assert len(pushes) == 1
assert pushes[0][-1].startswith("HEAD:refs/heads/ephemeral/hermes-handoff-acceptance/")
assert pushes[0][0] == "kubectl" and "exec" in pushes[0] # pushed as the worker, not the operator
assert any("DELETE" in call for call in cluster.calls)
def test_the_report_is_written_to_stdout_when_no_output_path_is_given(
monkeypatch, capsys
) -> None:
status, _ = run_cli(monkeypatch, ["--json-only"], HEALTHY_CLUSTER)
payload = json.loads(capsys.readouterr().out)
assert status == acceptance.EXIT_NO_GO
assert payload["harness"] == model.HARNESS
def test_the_human_summary_accompanies_the_json_unless_suppressed(
monkeypatch, capsys, tmp_path
) -> None:
output = tmp_path / "report.json"
run_cli(monkeypatch, ["--output", str(output)], HEALTHY_CLUSTER)
captured = capsys.readouterr()
assert model.HARNESS in captured.err
assert "vantage operator" in captured.err
assert captured.out == ""
def test_a_report_that_fails_its_own_screening_is_replaced_not_published(tmp_path) -> None:
"""The last barrier: never write an artifact nobody re-read."""
output = tmp_path / "report.json"
arguments = acceptance.build_parser().parse_args(["--output", str(output), "--json-only"])
report = model.Report(mode="read-only", started_at="2026-08-17T00:00:00Z")
report.harness_errors = ["leaked ghp_ABCDEFGHIJKLMNOPQRSTUV1234"]
monkey = report.as_dict
report.as_dict = lambda *_args, **_kwargs: { # type: ignore[method-assign]
**monkey(),
"leak": "token: ghp_ABCDEFGHIJKLMNOPQRSTUV1234",
}
offenders = acceptance.emit(report, arguments)
payload = read_report(output)
assert offenders == ["$.leak"]
assert payload["decision"] == model.NO_GO
assert "leak" not in payload
assert "failed its own credential screening" in payload["harness_errors"][0]
def test_a_screening_failure_forces_no_go_from_the_entry_point(monkeypatch, tmp_path) -> None:
"""The exit status must follow the refusal to publish, not the check tally."""
output = tmp_path / "report.json"
monkeypatch.setattr(acceptance, "unscreened_fields", lambda _payload: ["$.checks[0].stdout"])
monkeypatch.setattr(acceptance, "build_catalog", lambda _targets: [])
monkeypatch.setattr(harness_run, "vantage_problems", lambda _records: [])
status, _ = run_cli(monkeypatch, ["--output", str(output), "--json-only"], HEALTHY_CLUSTER)
assert status == acceptance.EXIT_NO_GO
assert read_report(output)["decision"] == model.NO_GO
def test_no_recorded_command_output_carries_a_credential_shape(monkeypatch, tmp_path) -> None:
output = tmp_path / "report.json"
leaky = completed(stdout="ANTHROPIC_API_KEY=sk-ant-abcdefghijklmnopqrstuvwxyz\n")
run_cli(
monkeypatch,
["--output", str(output), "--json-only"],
[*HEALTHY_CLUSTER, ("exec", leaky)],
)
assert model.unscreened_fields(read_report(output)) == []
assert "sk-ant-abcdefghijklmnopqrstuvwxyz" not in output.read_text(encoding="utf-8")
@pytest.mark.parametrize("flag", ["--help"])
def test_the_help_text_describes_the_read_only_default(flag: str, capsys) -> None:
with pytest.raises(SystemExit):
acceptance.main([flag])
assert "Read-only by default" in capsys.readouterr().out
def test_go_is_reachable_when_nothing_mandatory_is_outstanding(monkeypatch, tmp_path) -> None:
"""Exercise the success exit path without pretending the cluster is clean."""
output = tmp_path / "report.json"
monkeypatch.setattr(harness_run, "build_catalog", lambda _targets: [])
monkeypatch.setattr(acceptance, "build_catalog", lambda _targets: [])
monkeypatch.setattr(harness_run, "vantage_problems", lambda _records: [])
status, _ = run_cli(monkeypatch, ["--output", str(output), "--json-only"], HEALTHY_CLUSTER)
assert status == acceptance.EXIT_GO
assert read_report(output)["decision"] == model.GO