feat(registrar): repair deterministic projections

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
This commit is contained in:
tegwick 2026-08-22 09:53:11 +02:00
parent 9bdc79a0d1
commit e656f7f6ee
3 changed files with 114 additions and 2 deletions

View file

@ -63,6 +63,12 @@ def main(argv: list[str] | None = None) -> int:
help="Confirm that --api-base is the authoritative hub",
)
p_registrar.add_argument("--push", action="store_true", help="Push the registrar commit")
p_registrar.add_argument(
"--repair-workplan",
default=None,
metavar="ID",
help="Rebuild and verify one already-identified workplan projection",
)
p_cmd = sub.add_parser(
"update-task-status",
@ -412,6 +418,7 @@ def main(argv: list[str] | None = None) -> int:
statehub_bin=args.statehub_bin,
confirm_primary=args.confirm_primary,
push=args.push,
repair_workplan=args.repair_workplan,
)
print(json.dumps(result.to_dict(), indent=2))
return 0 if result.status in {"applied", "noop"} else 1

View file

@ -141,6 +141,34 @@ def _check_primary(api_base: str) -> tuple[dict[str, Any], str | None]:
return payload, None
def _workplan_projection_id(repo: Path, canonical_id: str) -> str | None:
"""Return the authoritative projection UUID for one open workplan."""
workplans_dir = repo / "workplans"
if not workplans_dir.is_dir():
return None
for path in sorted(workplans_dir.glob("*.md")):
parsed = parse_workplan_file(path, repo_root=repo)
if parsed.id != canonical_id:
continue
return parsed.state_hub_workstream_id or None
return None
def _projection_exists(api_base: str, projection_id: str) -> tuple[dict[str, Any], str | None]:
try:
response = httpx.get(
f"{api_base.rstrip('/')}/workplans/{projection_id}",
timeout=10.0,
)
response.raise_for_status()
payload = response.json()
except (httpx.HTTPError, ValueError) as exc:
return {}, f"workplan projection verification failed: {exc}"
if str(payload.get("id")) != projection_id:
return payload, "workplan projection returned an unexpected identity"
return payload, None
def _run_statehub(command: list[str], *, env: dict[str, str]) -> subprocess.CompletedProcess[str]:
return subprocess.run(
command,
@ -159,6 +187,7 @@ def registrar_reconcile(
statehub_bin: str | None = None,
confirm_primary: bool = False,
push: bool = False,
repair_workplan: str | None = None,
) -> RegistrarResult:
"""Register missing workplan/task UUIDs through one scoped child process."""
cid = str(uuid.uuid4())
@ -171,6 +200,22 @@ def registrar_reconcile(
"missing_before": before,
}
repair_projection_id = None
if repair_workplan:
repair_projection_id = _workplan_projection_id(repo, repair_workplan)
evidence["repair_workplan"] = repair_workplan
evidence["repair_projection_id"] = repair_projection_id
if not repair_projection_id:
return RegistrarResult(
"rejected",
evidence,
{
"code": "repair_target_invalid",
"message": "repair workplan is absent or has no authoritative projection UUID",
},
cid,
)
if not confirm_primary:
return RegistrarResult(
"rejected",
@ -181,7 +226,7 @@ def registrar_reconcile(
},
cid,
)
if not any(before.values()):
if not any(before.values()) and not repair_projection_id:
evidence["missing_after"] = before
return RegistrarResult("noop", evidence, None, cid)
@ -236,7 +281,21 @@ def registrar_reconcile(
evidence["statehub_stderr_tail"] = completed.stderr[-2000:]
after = _missing_identifiers(repo)
evidence["missing_after"] = after
if completed.returncode not in {0, 2} or any(after.values()):
repair_verified = False
if repair_projection_id:
projection, projection_error = _projection_exists(api_base, repair_projection_id)
evidence["repair_projection"] = projection
evidence["repair_projection_verified"] = projection_error is None
if projection_error:
evidence["repair_projection_error"] = projection_error
repair_verified = projection_error is None
accepted_exit_codes = {0, 2}
if repair_verified:
# A repository-scoped projection repair may coexist with legacy stale
# references that correctly keep the broader consistency report red.
accepted_exit_codes.add(1)
if completed.returncode not in accepted_exit_codes or any(after.values()):
return RegistrarResult(
"failed",
evidence,

View file

@ -134,3 +134,49 @@ def test_scopes_registrar_env_and_commits_assigned_ids(tmp_path: Path, monkeypat
["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_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")
_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"
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"