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>
125 lines
3.8 KiB
Python
125 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
from datetime import date
|
|
from pathlib import Path
|
|
|
|
from rein_aharness import brief_daily
|
|
|
|
|
|
def _git_repo(tmp_path: Path) -> Path:
|
|
repo = tmp_path / "binky-control"
|
|
repo.mkdir()
|
|
subprocess.run(["git", "init"], cwd=repo, check=True, capture_output=True)
|
|
subprocess.run(
|
|
["git", "config", "user.email", "test@example.com"],
|
|
cwd=repo,
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
subprocess.run(
|
|
["git", "config", "user.name", "test"],
|
|
cwd=repo,
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
(repo / "DecisionQueue.md").write_text(
|
|
"# DecisionQueue\n\n## Open\n\n### DEC-2026-999\nstatus: prepared\n",
|
|
encoding="utf-8",
|
|
)
|
|
(repo / "OfficeHourQueue.md").write_text("# OH\n", encoding="utf-8")
|
|
(repo / "AutopilotWorkQueue.md").write_text("# AWQ\n", encoding="utf-8")
|
|
(repo / "RiskRegister.md").write_text("# Risks\n", encoding="utf-8")
|
|
(repo / "briefs").mkdir()
|
|
subprocess.run(["git", "add", "."], cwd=repo, check=True, capture_output=True)
|
|
subprocess.run(
|
|
["git", "commit", "-m", "init"],
|
|
cwd=repo,
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
return repo
|
|
|
|
|
|
def test_render_brief_format() -> None:
|
|
md = brief_daily.render_brief(
|
|
date(2026, 7, 22),
|
|
{
|
|
"decide_now": ["DEC-1 approve cutover"],
|
|
"progress": ["WP-0005 finished"],
|
|
"risks": [],
|
|
"best_next_hour": "Approve DEC-1",
|
|
},
|
|
)
|
|
assert md.startswith("# Daily Brief — 2026-07-22\n")
|
|
assert "## Decide now" in md
|
|
assert "## Progress" in md
|
|
assert "## Risks" in md
|
|
assert "## Best next hour" in md
|
|
assert "No RiskRegister changes" in md
|
|
assert "Approve DEC-1" in md
|
|
|
|
|
|
def test_parse_brief_response_fences() -> None:
|
|
data = brief_daily.parse_brief_response(
|
|
'```json\n{"decide_now":[],"progress":["x"],"risks":[],'
|
|
'"best_next_hour":"none"}\n```'
|
|
)
|
|
assert data["progress"] == ["x"]
|
|
|
|
|
|
def test_run_brief_daily_mock(tmp_path: Path, monkeypatch) -> None:
|
|
repo = _git_repo(tmp_path)
|
|
monkeypatch.setattr(brief_daily.hub, "post_progress_event", lambda **kw: True)
|
|
day = date(2026, 7, 22)
|
|
|
|
def fake(prompt: str) -> str:
|
|
assert "DecisionQueue" in prompt
|
|
return json.dumps(
|
|
{
|
|
"decide_now": ["DEC-2026-999 go"],
|
|
"progress": ["scaffold landed"],
|
|
"risks": [],
|
|
"best_next_hour": "no founder action needed today",
|
|
}
|
|
)
|
|
|
|
result = brief_daily.run_brief_daily(
|
|
repo, day=day, complete_fn=fake, force=True
|
|
)
|
|
assert result.ok
|
|
assert result.wrote
|
|
assert result.committed
|
|
path = repo / "briefs" / "2026-07-22-daily-brief.md"
|
|
assert path.is_file()
|
|
text = path.read_text(encoding="utf-8")
|
|
assert "DEC-2026-999" in text
|
|
assert "scaffold landed" in text
|
|
|
|
|
|
def test_skip_existing_brief(tmp_path: Path, monkeypatch) -> None:
|
|
repo = _git_repo(tmp_path)
|
|
monkeypatch.setattr(brief_daily.hub, "post_progress_event", lambda **kw: True)
|
|
day = date(2026, 7, 22)
|
|
path = brief_daily.brief_path_for(repo, day)
|
|
path.write_text("# Daily Brief — 2026-07-22\n\nalready\n", encoding="utf-8")
|
|
subprocess.run(["git", "add", "."], cwd=repo, check=True, capture_output=True)
|
|
subprocess.run(
|
|
["git", "commit", "-m", "brief"],
|
|
cwd=repo,
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
called = {"n": 0}
|
|
|
|
def boom(prompt: str) -> str:
|
|
called["n"] += 1
|
|
raise AssertionError("should not call LLM")
|
|
|
|
result = brief_daily.run_brief_daily(
|
|
repo, day=day, complete_fn=boom, force=False
|
|
)
|
|
assert result.ok
|
|
assert result.skipped_existing
|
|
assert called["n"] == 0
|