Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
136 lines
4.1 KiB
Python
136 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
from repo_manager.commands import registrar_reconcile as rr
|
|
|
|
|
|
def _git(repo: Path, *args: str) -> None:
|
|
subprocess.run(["git", *args], cwd=repo, check=True, capture_output=True)
|
|
|
|
|
|
def _fixture(tmp_path: Path) -> Path:
|
|
bare = tmp_path / "remote.git"
|
|
repo = tmp_path / "demo"
|
|
_git(tmp_path, "init", "--bare", str(bare))
|
|
_git(tmp_path, "clone", str(bare), str(repo))
|
|
_git(repo, "config", "user.name", "Test")
|
|
_git(repo, "config", "user.email", "test@example.com")
|
|
(repo / "workplans").mkdir()
|
|
(repo / "workplans" / "DEMO-WP-0001.md").write_text(
|
|
"""---
|
|
id: DEMO-WP-0001
|
|
type: workplan
|
|
title: Demo
|
|
status: active
|
|
---
|
|
|
|
## Task
|
|
|
|
```task
|
|
id: DEMO-WP-0001-T01
|
|
status: todo
|
|
priority: high
|
|
```
|
|
""",
|
|
encoding="utf-8",
|
|
)
|
|
_git(repo, "add", ".")
|
|
_git(repo, "commit", "-m", "seed")
|
|
_git(repo, "push", "-u", "origin", "HEAD")
|
|
return repo
|
|
|
|
|
|
def test_requires_explicit_primary_confirmation(tmp_path: Path) -> None:
|
|
repo = _fixture(tmp_path)
|
|
result = rr.registrar_reconcile(repo)
|
|
assert result.status == "rejected"
|
|
assert result.error and result.error["code"] == "confirmation_required"
|
|
|
|
|
|
def test_closed_records_are_not_registrar_work(tmp_path: Path) -> None:
|
|
repo = _fixture(tmp_path)
|
|
workplan = repo / "workplans" / "DEMO-WP-0001.md"
|
|
workplan.write_text(
|
|
workplan.read_text(encoding="utf-8").replace("status: active", "status: finished"),
|
|
encoding="utf-8",
|
|
)
|
|
assert rr._missing_identifiers(repo) == {
|
|
"workplans": [],
|
|
"tasks": [],
|
|
"intakes": [],
|
|
"decisions": [],
|
|
}
|
|
|
|
|
|
def test_rejects_dirty_or_unsynced_repository(tmp_path: Path, monkeypatch) -> None:
|
|
repo = _fixture(tmp_path)
|
|
monkeypatch.setattr(rr, "_check_primary", lambda _api: ({"status": "ok", "db": "connected"}, None))
|
|
(repo / "note.txt").write_text("dirty", encoding="utf-8")
|
|
dirty = rr.registrar_reconcile(repo, confirm_primary=True)
|
|
assert dirty.status == "rejected"
|
|
assert dirty.error and dirty.error["code"] == "git_precondition_failed"
|
|
|
|
|
|
def test_missing_scan_includes_intakes_and_decisions(tmp_path: Path) -> None:
|
|
repo = _fixture(tmp_path)
|
|
records = repo / "intakes" / "records.md"
|
|
records.parent.mkdir()
|
|
records.write_text(
|
|
"""# Records
|
|
|
|
```yaml
|
|
id: DEMO-IN-0001
|
|
kind: intake
|
|
title: Intake
|
|
status: open
|
|
```
|
|
|
|
```yaml
|
|
id: DEMO-DEC-0001
|
|
kind: decision
|
|
title: Decision
|
|
status: open
|
|
```
|
|
""",
|
|
encoding="utf-8",
|
|
)
|
|
missing = rr._missing_identifiers(repo)
|
|
assert missing["intakes"] == ["DEMO-IN-0001"]
|
|
assert missing["decisions"] == ["DEMO-DEC-0001"]
|
|
|
|
|
|
def test_scopes_registrar_env_and_commits_assigned_ids(tmp_path: Path, monkeypatch) -> None:
|
|
repo = _fixture(tmp_path)
|
|
monkeypatch.setattr(rr, "_check_primary", lambda _api: ({"status": "ok", "db": "connected"}, None))
|
|
|
|
def fake_run(command, *, env):
|
|
assert command[1:3] == ["fix-consistency", "--path"]
|
|
assert env["STATEHUB_REGISTRAR"] == "1"
|
|
workplan = repo / "workplans" / "DEMO-WP-0001.md"
|
|
text = workplan.read_text(encoding="utf-8")
|
|
text = text.replace("status: active\n---", 'status: active\nstate_hub_workstream_id: "11111111-1111-4111-8111-111111111111"\n---')
|
|
text = text.replace("priority: high\n```", 'priority: high\nstate_hub_task_id: "22222222-2222-4222-8222-222222222222"\n```')
|
|
workplan.write_text(text, encoding="utf-8")
|
|
return subprocess.CompletedProcess(command, 0, "ok", "")
|
|
|
|
monkeypatch.setattr(rr, "_run_statehub", fake_run)
|
|
|
|
result = rr.registrar_reconcile(
|
|
repo,
|
|
statehub_bin="statehub",
|
|
confirm_primary=True,
|
|
)
|
|
|
|
assert result.status == "applied"
|
|
assert result.evidence["missing_after"] == {
|
|
"workplans": [],
|
|
"tasks": [],
|
|
"intakes": [],
|
|
"decisions": [],
|
|
}
|
|
subject = subprocess.run(
|
|
["git", "log", "-1", "--format=%s"], cwd=repo, capture_output=True, text=True, check=True
|
|
).stdout.strip()
|
|
assert subject == "chore(registrar): assign State Hub identifiers"
|