feat: brief-daily via llm-connect (structured daily brief)
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).
This commit is contained in:
parent
7520a53831
commit
f770825975
4 changed files with 536 additions and 0 deletions
125
tests/test_brief_daily.py
Normal file
125
tests/test_brief_daily.py
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue