repo-manager/tests/test_registrar_reconcile.py
tegwick 7b9fdaa734 fix(registrar): bound slow consistency runs
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02b22-9638-76d2-bbff-b7ea1770b118
2026-08-23 11:58:43 +02:00

556 lines
17 KiB
Python

from __future__ import annotations
import subprocess
from pathlib import Path
import pytest
from repo_manager.commands import registrar_reconcile as rr
@pytest.fixture(autouse=True)
def isolate_registrar_lock(tmp_path: Path, monkeypatch) -> None:
"""Keep unit tests independent from a live workstation registrar run."""
monkeypatch.setattr(rr, "LOCK_PATH", tmp_path / "registrar.lock")
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_unrelated_historical_collision_does_not_block_scoped_request(
tmp_path: Path,
) -> None:
repo = _fixture(tmp_path)
for number, task_uuid in (
(2, "22222222-2222-4222-8222-222222222222"),
(3, "33333333-3333-4333-8333-333333333333"),
):
(repo / "workplans" / f"DEMO-WP-000{number}.md").write_text(
f"""---
id: DEMO-WP-000{number}
type: workplan
title: Historical {number}
status: finished
state_hub_workstream_id: "{number}{'1' * 7}-1111-4111-8111-111111111111"
---
## Historical task
```task
id: DEMO-WP-9999-T01
status: done
priority: low
state_hub_task_id: "{task_uuid}"
```
""",
encoding="utf-8",
)
result = rr.registrar_reconcile(repo)
assert result.status == "rejected"
assert result.error and result.error["code"] == "confirmation_required"
assert result.evidence["record_identity"]["identity_collisions"]
assert result.evidence["blocking_identity_collisions"] == []
def test_requested_collision_still_fails_closed(tmp_path: Path) -> None:
repo = _fixture(tmp_path)
second = repo / "workplans" / "DEMO-WP-0002.md"
second.write_text(
"""---
id: DEMO-WP-0002
type: workplan
title: Conflicting request
status: active
---
## Conflicting task
```task
id: DEMO-WP-0001-T01
status: todo
priority: high
```
""",
encoding="utf-8",
)
result = rr.registrar_reconcile(repo)
assert result.status == "rejected"
assert result.error and result.error["code"] == "record_identity_collision"
assert result.evidence["blocking_identity_collisions"][0]["id"] == "DEMO-WP-0001-T01"
def test_requested_invalid_identifier_fails_before_registration(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("DEMO-WP-0001", "DEMO-INVALID"),
encoding="utf-8",
)
result = rr.registrar_reconcile(repo)
assert result.status == "rejected"
assert result.error and result.error["code"] == "record_identifier_invalid"
assert result.evidence["blocking_invalid_identifiers"][0]["id"] == "DEMO-INVALID"
def test_unlinked_closed_workplan_is_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": ["DEMO-WP-0001"],
"tasks": ["DEMO-WP-0001-T01"],
"intakes": [],
"decisions": [],
}
def test_linked_closed_workplan_does_not_reopen_historical_task_gaps(tmp_path: Path) -> None:
repo = _fixture(tmp_path)
workplan = repo / "workplans" / "DEMO-WP-0001.md"
text = workplan.read_text(encoding="utf-8")
text = text.replace(
"status: active\n---",
'status: finished\nstate_hub_workstream_id: "11111111-1111-4111-8111-111111111111"\n---',
)
workplan.write_text(text, 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_missing_scan_includes_lowercase_top_level_record_files(tmp_path: Path) -> None:
repo = _fixture(tmp_path)
(repo / "intakes.md").write_text(
"""# Intakes
```yaml
id: DEMO-IN-0002
kind: intake
title: Standalone intake
status: open
```
""",
encoding="utf-8",
)
(repo / "decisions.md").write_text(
"""# Decisions
```yaml
id: DEMO-DEC-0002
kind: decision
title: Standalone decision
status: accepted
```
""",
encoding="utf-8",
)
missing = rr._missing_identifiers(repo)
assert missing["intakes"] == ["DEMO-IN-0002"]
assert missing["decisions"] == ["DEMO-DEC-0002"]
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"
def test_accepts_unrelated_assessment_fail_after_exact_requested_verification(
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):
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, 1, "unrelated C-03 remains", "")
monkeypatch.setattr(rr, "_run_statehub", fake_run)
monkeypatch.setattr(
rr,
"_verify_full_projection",
lambda _api, workplans, tasks, records: (
{
"expected_workplans": len(workplans),
"expected_tasks": len(tasks),
"expected_intakes": len(records["intake"]),
"expected_decisions": len(records["decision"]),
"missing_workplans": [],
"missing_tasks": [],
"missing_intakes": [],
"missing_decisions": [],
},
None,
),
)
result = rr.registrar_reconcile(
repo,
statehub_bin="statehub",
confirm_primary=True,
)
assert result.status == "applied"
assert result.evidence["requested_projection_verified"] is True
assert result.evidence["requested_projection"]["expected_workplans"] == 1
assert result.evidence["requested_projection"]["expected_tasks"] == 1
def test_statehub_timeout_returns_structured_failure(tmp_path: Path, monkeypatch) -> None:
repo = _fixture(tmp_path)
monkeypatch.setattr(
rr,
"_check_primary",
lambda _api: ({"status": "ok", "db": "connected"}, None),
)
monkeypatch.setattr(
rr,
"_run_statehub",
lambda command, *, env: (_ for _ in ()).throw(
subprocess.TimeoutExpired(command, rr.STATEHUB_TIMEOUT_SECONDS, output="partial")
),
)
result = rr.registrar_reconcile(
repo,
statehub_bin="statehub",
confirm_primary=True,
)
assert result.status == "failed"
assert result.error and result.error["code"] == "statehub_timeout"
assert result.evidence["statehub_stdout_tail"] == "partial"
assert result.evidence["missing_after"] == result.evidence["missing_before"]
def test_repairs_an_already_identified_workplan_projection(tmp_path: Path, monkeypatch) -> None:
repo = _fixture(tmp_path)
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")
brief = repo / ".custodian-brief.md"
brief.write_text("authoritative local brief\n", encoding="utf-8")
_git(repo, "add", ".")
_git(repo, "commit", "-m", "add authoritative identifiers")
_git(repo, "push")
monkeypatch.setattr(
rr,
"_check_primary",
lambda _api: ({"status": "ok", "db": "connected"}, None),
)
monkeypatch.setattr(
rr,
"_projection_exists",
lambda _api, projection_id: ({"id": projection_id}, None),
)
def fake_run(command, *, env):
assert env["STATEHUB_REGISTRAR"] == "1"
brief.write_text("generated from partial projection\n", encoding="utf-8")
return subprocess.CompletedProcess(command, 1, "legacy stale references remain", "")
monkeypatch.setattr(rr, "_run_statehub", fake_run)
result = rr.registrar_reconcile(
repo,
statehub_bin="statehub",
confirm_primary=True,
repair_workplan="DEMO-WP-0001",
)
assert result.status == "applied"
assert result.evidence["repair_projection_verified"] is True
assert result.evidence["repair_projection_id"] == "11111111-1111-4111-8111-111111111111"
assert result.evidence["restored_generated_paths"] == [".custodian-brief.md"]
assert brief.read_text(encoding="utf-8") == "authoritative local brief\n"
def test_repair_fails_when_exact_projection_remains_absent(tmp_path: Path, monkeypatch) -> None:
repo = _fixture(tmp_path)
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")
_git(repo, "add", ".")
_git(repo, "commit", "-m", "add authoritative identifiers")
_git(repo, "push")
monkeypatch.setattr(
rr,
"_check_primary",
lambda _api: ({"status": "ok", "db": "connected"}, None),
)
monkeypatch.setattr(
rr,
"_projection_exists",
lambda _api, _projection_id: ({}, "workplan projection returned 404"),
)
monkeypatch.setattr(
rr,
"_run_statehub",
lambda command, *, env: subprocess.CompletedProcess(
command, 1, "stale reference remains", ""
),
)
result = rr.registrar_reconcile(
repo,
statehub_bin="statehub",
confirm_primary=True,
repair_workplan="DEMO-WP-0001",
)
assert result.status == "failed"
assert result.error and result.error["code"] == "registration_incomplete"
assert result.evidence["repair_projection_verified"] is False
assert "requested_projection_verified" not in result.evidence
def test_bootstraps_and_verifies_a_completely_empty_projection(tmp_path: Path, monkeypatch) -> None:
repo = _fixture(tmp_path)
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")
_git(repo, "add", ".")
_git(repo, "commit", "-m", "add authoritative identifiers")
_git(repo, "push")
monkeypatch.setattr(
rr,
"_check_primary",
lambda _api: ({"status": "ok", "db": "connected"}, None),
)
monkeypatch.setattr(
rr,
"_check_empty_repo_projection",
lambda _api, _slug: ({"repo_id": "repo-1", "workplan_count": 0}, None),
)
monkeypatch.setattr(
rr,
"_verify_full_projection",
lambda _api, workplans, tasks, records: (
{
"expected_workplans": len(workplans),
"expected_tasks": len(tasks),
"expected_intakes": len(records["intake"]),
"expected_decisions": len(records["decision"]),
"missing_workplans": [],
"missing_tasks": [],
"missing_intakes": [],
"missing_decisions": [],
},
None,
),
)
def fake_run(command, *, env):
assert command[-1] == "--bootstrap-empty-projection"
assert env["STATEHUB_REGISTRAR"] == "1"
return subprocess.CompletedProcess(command, 2, "bootstrap warnings", "")
monkeypatch.setattr(rr, "_run_statehub", fake_run)
result = rr.registrar_reconcile(
repo,
statehub_bin="statehub",
confirm_primary=True,
bootstrap_empty_projection=True,
)
assert result.status == "applied"
assert result.evidence["bootstrap_source"] == {
"workplans": 1,
"tasks": 1,
"intakes": 0,
"decisions": 0,
}
assert result.evidence["bootstrap_projection_verified"] is True
def test_empty_projection_bootstrap_refuses_existing_rows(tmp_path: Path, monkeypatch) -> None:
repo = _fixture(tmp_path)
monkeypatch.setattr(
rr,
"_check_primary",
lambda _api: ({"status": "ok", "db": "connected"}, None),
)
monkeypatch.setattr(
rr,
"_check_empty_repo_projection",
lambda _api, _slug: (
{"repo_id": "repo-1", "workplan_count": 1},
"target repository projection is not empty",
),
)
monkeypatch.setattr(
rr,
"_run_statehub",
lambda *_args, **_kwargs: pytest.fail("non-empty bootstrap must not run State Hub"),
)
result = rr.registrar_reconcile(
repo,
confirm_primary=True,
bootstrap_empty_projection=True,
)
assert result.status == "rejected"
assert result.error and result.error["code"] == "bootstrap_precondition_failed"