Add lifecycle renormalization consistency repair

This commit is contained in:
2026-05-23 16:54:38 +02:00
parent 6496ef851a
commit 0aa02d9117
4 changed files with 284 additions and 7 deletions

View File

@@ -28,10 +28,13 @@ from consistency_check import (
_BACKGROUND_CHECKS,
_detect_behind_remote,
_git_pull,
_patch_frontmatter_field,
_patch_task_status_in_file,
_report_needs_action,
archive_closed_workplans,
canonical_workplan_filename,
check_repo,
fix_repo,
get_tasks_from_workplan,
iter_workplan_files,
normalise_workstream_status,
@@ -726,6 +729,133 @@ class TestPatchTaskStatusInFile:
assert result is False
class TestPatchFrontmatterField:
def test_patches_existing_scalar_field(self, tmp_path):
f = tmp_path / "workplan.md"
f.write_text("---\nid: WP-001\nstatus: proposed\n---\nBody\n", encoding="utf-8")
assert _patch_frontmatter_field(f, "status", "active") is True
patched = f.read_text(encoding="utf-8")
assert "status: active" in patched
assert "id: WP-001" in patched
def test_is_idempotent_when_field_already_matches(self, tmp_path):
f = tmp_path / "workplan.md"
f.write_text("---\nid: WP-001\nstatus: active\n---\nBody\n", encoding="utf-8")
assert _patch_frontmatter_field(f, "status", "active") is False
assert f.read_text(encoding="utf-8").count("status: active") == 1
class TestLifecycleRenormalization:
def _make_repo(self, tmp_path, status: str = "proposed") -> Path:
repo = tmp_path / "repo"
workplans = repo / "workplans"
workplans.mkdir(parents=True)
(workplans / "STATE-WP-0001-demo.md").write_text(
"---\n"
"id: STATE-WP-0001\n"
"type: workplan\n"
"title: Demo\n"
"domain: custodian\n"
"repo: state-hub\n"
f"status: {status}\n"
"owner: codex\n"
"state_hub_workstream_id: \"ws-1\"\n"
"---\n\n"
"## Implement Demo\n\n"
"```task\n"
"id: STATE-WP-0001-T01\n"
"status: in_progress\n"
"priority: high\n"
"state_hub_task_id: \"task-1\"\n"
"```\n",
encoding="utf-8",
)
return repo
def _api_get_for_repo(self, repo: Path):
ws = {
"id": "ws-1",
"repo_id": "repo-1",
"topic_id": "topic-1",
"slug": "state-wp-0001",
"title": "Demo",
"status": "proposed",
"planning_priority": None,
"planning_order": None,
}
task = {
"id": "task-1",
"title": "Implement Demo",
"status": "in_progress",
"description": None,
}
def fake_get(_api_base, path, params=None):
if path == "/repos/state-hub":
import socket
return {
"id": "repo-1",
"slug": "state-hub",
"local_path": str(repo),
"host_paths": {socket.gethostname(): str(repo)},
}
if path == "/workstreams/ws-1":
return ws
if path == "/tasks/task-1":
return task
if path == "/tasks" and params == {"workstream_id": "ws-1"}:
return [task]
if path == "/workstreams/ws-1/dependencies":
return []
if path == "/workstreams" and params == {"repo_id": "repo-1"}:
return [ws]
if path == "/workstreams" and params and params.get("topic_id") == "topic-1":
return []
return []
return fake_get
def test_active_task_in_planning_workplan_reports_c23_not_c04(self, tmp_path, monkeypatch):
repo = self._make_repo(tmp_path)
monkeypatch.setattr("consistency_check._api_get", self._api_get_for_repo(repo))
report = check_repo("http://unused", "state-hub")
check_ids = [issue.check_id for issue in report.issues]
assert "C-23" in check_ids
assert "C-04" not in check_ids
issue = next(issue for issue in report.issues if issue.check_id == "C-23")
assert issue.fixable is True
assert issue.file_value == "proposed"
def test_fix_repo_repairs_planning_workplan_with_active_task(self, tmp_path, monkeypatch):
repo = self._make_repo(tmp_path)
wp = repo / "workplans" / "STATE-WP-0001-demo.md"
patches = []
def fake_patch(_api_base, path, body):
patches.append((path, body))
return {"ok": True}
monkeypatch.setattr("consistency_check._api_get", self._api_get_for_repo(repo))
monkeypatch.setattr("consistency_check._api_patch", fake_patch)
monkeypatch.setattr("consistency_check._detect_behind_remote", lambda _repo_path: False)
monkeypatch.setattr("consistency_check._detect_ahead_of_remote", lambda _repo_path: 0)
monkeypatch.setattr("consistency_check._git_commit_writeback", lambda *args, **kwargs: True)
monkeypatch.setattr("consistency_check._write_custodian_brief", lambda *args, **kwargs: False)
monkeypatch.setattr("consistency_check._git_push", lambda _repo_path: (True, "pushed"))
report = fix_repo("http://unused", "state-hub")
assert ("/workstreams/ws-1", {"status": "active"}) in patches
assert "status: active" in wp.read_text(encoding="utf-8")
assert any("C-23 fixed" in fix for fix in report.fixes_applied)
# ---------------------------------------------------------------------------
# _git_pull (T02 remote fix helper)
# ---------------------------------------------------------------------------