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"