rein-aharness/tests/test_manifest.py
tegwick f6930ad115 Rename package, CLI, and deploy artifacts to rein-aharness (HARNESS-WP-0002-T02)
agent_harness -> rein_aharness (package + all imports), CLI command
agent-harness -> rein-aharness, Docker image tag, k8s namespace/labels/
names, Makefile targets, deploy script env var/paths. In-repo identity
strings (hub event source, metrics harness field, default assignee,
argparse prog name, commit author identity) updated to match.

Historical documents left untouched on purpose: docs/adr/ADR-001-agent-harness-architecture.md,
docs/architecture.md (dated v0.1 snapshot), workplans/HARNESS-WP-0001
(completed under the old name), and the SSH host alias
"forgejo-agent-harness" (external ~/.ssh/config entry, not owned here).

Verified: 47/47 tests pass, CLI runs correctly from a fresh venv,
`make image` builds and the resulting container runs correctly.

deploy/README.md gained an explicit rename cutover checklist for what
this session cannot safely do unattended -- moving the host-side
secrets dir and checkout on railiance01, and not deleting the old k8s
namespace until the new one is confirmed working. The actual live
cutover (running that checklist against the real Railiance deployment)
is not attempted here -- real production surgery on binky-control's
live automation, needs the operator present.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 14:22:18 +02:00

226 lines
6.2 KiB
Python

from __future__ import annotations
from pathlib import Path
import pytest
import yaml
from rein_aharness.cli import main
from rein_aharness.manifest import (
HARNESS_MAJOR,
ManifestError,
load_manifest,
parse_manifest,
resolve_run_policy,
validate_manifest,
)
from rein_aharness.profiles import UnknownToolProfileError, get_profile, list_profiles
def _write_manifest(tmp_path: Path, data: dict) -> Path:
kaizen = tmp_path / ".kaizen"
kaizen.mkdir()
path = kaizen / "schedule.yml"
path.write_text(yaml.safe_dump(data, sort_keys=False), encoding="utf-8")
return path
def test_parse_and_validate_adr005_only() -> None:
manifest = parse_manifest(
{
"version": "1",
"timezone": "Europe/Berlin",
"agents": {
"coach": {"cadence": "weekly", "enabled": True},
},
}
)
assert validate_manifest(manifest) == []
assert validate_manifest(manifest, require_harness_fields=True)
def test_validate_harness_extensions() -> None:
manifest = parse_manifest(
{
"version": "1",
"harness": HARNESS_MAJOR,
"agents": {
"coach": {
"cadence": "daily",
"enabled": True,
"lane": "green",
"tool_profile": "green-commit-only",
"budget": 50000,
},
"mail": {
"cadence": "weekly",
"lane": "blue",
"tool_profile": "blue-mail-triage",
"budget": 10000,
},
},
}
)
assert validate_manifest(manifest) == []
assert validate_manifest(manifest, require_harness_fields=True) == []
def test_unknown_tool_profile_errors() -> None:
manifest = parse_manifest(
{
"version": "1",
"agents": {
"coach": {
"cadence": "daily",
"tool_profile": "does-not-exist",
}
},
}
)
errors = validate_manifest(manifest)
assert any("unknown tool_profile" in e for e in errors)
def test_lane_profile_mismatch() -> None:
manifest = parse_manifest(
{
"version": "1",
"agents": {
"coach": {
"cadence": "daily",
"lane": "blue",
"tool_profile": "green-commit-only",
}
},
}
)
errors = validate_manifest(manifest)
assert any("does not match tool_profile" in e for e in errors)
def test_budget_must_be_positive() -> None:
with pytest.raises(ManifestError, match="budget"):
parse_manifest(
{
"version": "1",
"agents": {"coach": {"cadence": "daily", "budget": 0}},
}
)
def test_harness_major_mismatch() -> None:
manifest = parse_manifest(
{
"version": "1",
"harness": HARNESS_MAJOR + 1,
"agents": {"coach": {"cadence": "daily"}},
}
)
errors = validate_manifest(manifest)
assert any("does not match this runtime" in e for e in errors)
def test_resolve_run_policy_defaults(tmp_path: Path) -> None:
profile, budget, lane, blueprint = resolve_run_policy(tmp_path, "coach")
assert profile == "green-commit-only"
assert budget is None
assert lane is None
assert blueprint == "coach"
def test_resolve_run_policy_from_manifest(tmp_path: Path) -> None:
_write_manifest(
tmp_path,
{
"version": "1",
"harness": HARNESS_MAJOR,
"agents": {
"coach": {
"cadence": "daily",
"tool_profile": "blue-mail-triage",
"lane": "blue",
"budget": 12000,
},
"mail-triage": {
"cadence": "weekly",
"blueprint": "coach",
"tool_profile": "blue-mail-triage",
"lane": "blue",
"budget": 1000,
},
},
},
)
profile, budget, lane, blueprint = resolve_run_policy(tmp_path, "coach")
assert profile == "blue-mail-triage"
assert budget == 12000
assert lane == "blue"
assert blueprint == "coach"
_, _, _, bp2 = resolve_run_policy(tmp_path, "mail-triage")
assert bp2 == "coach"
def test_resolve_unknown_profile_refuses(tmp_path: Path) -> None:
_write_manifest(
tmp_path,
{
"version": "1",
"agents": {
"coach": {
"cadence": "daily",
"tool_profile": "nope",
}
},
},
)
with pytest.raises(UnknownToolProfileError):
resolve_run_policy(tmp_path, "coach")
def test_load_manifest_missing(tmp_path: Path) -> None:
with pytest.raises(ManifestError, match="not found"):
load_manifest(tmp_path / ".kaizen" / "schedule.yml")
def test_profiles_registry() -> None:
names = {p.name for p in list_profiles()}
assert names == {"green-commit-only", "blue-mail-triage"}
green = get_profile("green-commit-only")
assert "git commit" in green.allowed_tools
assert "git push" not in green.allowed_tools
def test_cli_validate_ok(tmp_path: Path) -> None:
_write_manifest(
tmp_path,
{
"version": "1",
"harness": HARNESS_MAJOR,
"agents": {
"coach": {
"cadence": "daily",
"lane": "green",
"tool_profile": "green-commit-only",
"budget": 1000,
}
},
},
)
assert main(["validate", "--target", str(tmp_path), "--strict"]) == 0
def test_cli_validate_fails_unknown_profile(tmp_path: Path) -> None:
_write_manifest(
tmp_path,
{
"version": "1",
"agents": {
"coach": {"cadence": "daily", "tool_profile": "missing"}
},
},
)
assert main(["validate", "--target", str(tmp_path)]) == 1
def test_cli_profiles() -> None:
assert main(["profiles"]) == 0