Generate OperatingRhythm-format briefs with OpenRouter-backed llm-connect JSON + deterministic markdown write/commit. Server path for Binky daily rhythm without host Claude (BINKY-WP-0006-T05).
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 agent_harness 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
|