atlas-iac/testing/tests/test_hermes_handoff_mutation_gate.py
Hermes Agent d208a89d9d fix(hermes): close the zero-evidence fail-open in absence checks
evaluate_names_absent returned PASS when its step exited 0 with no output,
so five mandatory checks - the ones asserting that provider API keys, forge
credentials, a cluster-admin binding, and shared coordinator state are
absent - could report a pass on no evidence and turn a NO_GO into a GO.
Both name rules now resolve their step through one guard in _line_step, so
zero observations are NOT_RUN. Regressions pin all five real catalog specs
plus both reachable silence paths: a POSIX pipeline whose status comes from
its last stage, and a drifted kubectl -o jsonpath. The pool claim projection
emits one <volume>=<claim> line per template volume so a volume without a
PVC still counts as an observation rather than reading as drift.

Also closes the review's reachable hardening and evidence defects:

- pin Gitea paths to atlas/titan-iac on an exact segment boundary and
  reject relative segments, including percent-encoded ones
- forbid impersonation structurally in every mode and vantage; the inner
  command of kubectl exec is re-checked rather than exempted, and
  validate_catalog no longer guards only the operator vantage
- drop flux and helm from the binary allowlist; they had no pinned release
  digest, so no allowlisted binary can now be admitted that the executor
  would refuse to attest
- remove the inert --concurrency and --expect-telegram-sessions flags and
  the dead concurrency bound; Telegram continuity stays mandatory
- read the ephemeral pull index page by page, treat the create response as
  an authoritative source for the pull number, close every number either
  source names, and surface residue_ref plus exact manual_cleanup commands
  when creation is uncertain
- keep executable_path and executable_sha256 on unrecorded bulk-evidence
  steps so withholding bytes never withholds binary attestation
- revert the repo-wide hygiene legacy-exception mechanism; the contract
  change here is purely additive and the four pre-existing over-cap files
  are left to the canonical contract change in PR #14/#15
- correct the runbook ruff format scope so the documented command passes

Split hermes_handoff_arming.py out of hermes_handoff_ephemeral.py to keep
both modules under the 500-line cap. All 16 handoff modules hold at least
95% line and branch coverage; the mutation gate is 13/13.

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

61 lines
1.7 KiB
Python

"""Tests for the deterministic Hermes handoff mutation gate."""
from __future__ import annotations
from pathlib import Path
from testing.quality_handoff_mutation import (
MUTATIONS,
Mutation,
MutationReport,
main,
run_gate,
)
ROOT = Path(__file__).parents[2]
def test_every_security_mutant_is_killed() -> None:
report = run_gate(ROOT)
assert report.issues == ()
assert report.score == 100.0
assert report.killed == report.total >= 9
def test_invalid_anchor_baseline_syntax_and_survivor_are_reported() -> None:
path = "hermes_handoff_model.py"
cases = (
Mutation("missing", path, "not present", "x", "pass"),
Mutation(
"baseline",
path,
"SCHEMA_VERSION = 1",
"SCHEMA_VERSION = 2",
"raise SystemExit(1)",
),
Mutation("syntax", path, "SCHEMA_VERSION = 1", "if", "pass"),
Mutation("survivor", path, "SCHEMA_VERSION = 1", "SCHEMA_VERSION = 2", "pass"),
)
report = run_gate(ROOT, cases)
assert report.killed == 0
assert len(report.issues) == 4
assert report.score == 0.0
def test_main_reports_a_perfect_score(capsys) -> None:
"""Every mutant must die, and the count must track the declared set."""
assert main() == 0
total = len(MUTATIONS)
assert total >= 13
assert f"{total}/{total} (100.00%)" in capsys.readouterr().out
def test_main_reports_mutation_issues(monkeypatch, capsys) -> None:
monkeypatch.setattr(
"testing.quality_handoff_mutation.run_gate",
lambda _root: MutationReport(1, 0, ("survivor",)),
)
assert main() == 1
assert "survivor" in capsys.readouterr().out