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>
226 lines
6.2 KiB
Python
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
|