feat(identifiers): verify batch 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 23:50:34 +02:00
parent e6cc18bf18
commit 055c6971ab
11 changed files with 2964 additions and 9 deletions

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import subprocess
from pathlib import Path
import httpx
import pytest
from repo_manager.identifiers import (
@ -213,10 +214,24 @@ def _push_fixture_to_upstream(repo: Path, remote: Path) -> None:
subprocess.run(["git", "push", "-u", "origin", "HEAD:main"], cwd=repo, check=True)
def test_migration_batch_plan_pins_clean_synchronized_repo(tmp_path: Path) -> None:
def test_migration_batch_plan_pins_clean_synchronized_repo(
tmp_path: Path, monkeypatch
) -> None:
repo = tmp_path / "one"
path = repo / "workplans" / "one.md"
_workplan(path, "ONE-WP-0001", "active")
path.write_text(
path.read_text(encoding="utf-8")
.replace(
"status: active\n---",
'status: active\nstate_hub_workstream_id: "11111111-1111-4111-8111-111111111111"\n---',
)
.replace(
"status: todo\n```",
'status: todo\nstate_hub_task_id: "22222222-2222-4222-8222-222222222222"\n```',
),
encoding="utf-8",
)
subprocess.run(["git", "init"], cwd=repo, check=True, capture_output=True)
subprocess.run(["git", "add", "."], cwd=repo, check=True, capture_output=True)
subprocess.run(
@ -228,15 +243,84 @@ def test_migration_batch_plan_pins_clean_synchronized_repo(tmp_path: Path) -> No
_push_fixture_to_upstream(repo, tmp_path / "remote.git")
plan = plan_identifier_migration(tmp_path, "helixforge")
batch = plan_identifier_migration_batch(plan, repo_slugs=["one"])
current_uuids = {
mapping["current_uuid"] for mapping in plan["repositories"][0]["mappings"]
}
def projection_get(self, url):
status = 200 if any(str(url).endswith(str(value)) for value in current_uuids) else 404
return httpx.Response(status, request=httpx.Request("GET", url))
monkeypatch.setattr(httpx.Client, "get", projection_get)
batch = plan_identifier_migration_batch(
plan,
repo_slugs=["one"],
projection_api_bases=["http://hub-one.test", "http://hub-two.test"],
)
assert batch["ok"] is True
assert batch["ready_for_approval"] is True
assert batch["apply_authorized"] is False
assert batch["approval_required"] is True
assert batch["projection_api_bases"] == [
"http://hub-one.test",
"http://hub-two.test",
]
assert batch["repositories"][0]["projection_preflight"]["ok"] is True
assert batch["repositories"][0]["git_preflight"]["upstream"] == "origin/main"
assert len(batch["batch_sha256"]) == 64
def projection_drift(self, url):
return httpx.Response(404, request=httpx.Request("GET", url))
monkeypatch.setattr(httpx.Client, "get", projection_drift)
verification = verify_identifier_migration_batch(batch, plan=plan)
assert verification["ok"] is False
assert verification["ready_for_decision"] is False
def test_migration_batch_plan_rejects_projection_gap(tmp_path: Path, monkeypatch) -> None:
repo = tmp_path / "one"
path = repo / "workplans" / "one.md"
_workplan(path, "ONE-WP-0001", "active")
path.write_text(
path.read_text(encoding="utf-8")
.replace(
"status: active\n---",
'status: active\nstate_hub_workstream_id: "11111111-1111-4111-8111-111111111111"\n---',
)
.replace(
"status: todo\n```",
'status: todo\nstate_hub_task_id: "22222222-2222-4222-8222-222222222222"\n```',
),
encoding="utf-8",
)
subprocess.run(["git", "init"], cwd=repo, check=True, capture_output=True)
subprocess.run(["git", "add", "."], cwd=repo, check=True, capture_output=True)
subprocess.run(
["git", "-c", "user.name=Test", "-c", "user.email=test@example.com", "commit", "-m", "seed"],
cwd=repo,
check=True,
capture_output=True,
)
_push_fixture_to_upstream(repo, tmp_path / "remote.git")
plan = plan_identifier_migration(tmp_path, "helixforge")
def projection_get(self, url):
return httpx.Response(404, request=httpx.Request("GET", url))
monkeypatch.setattr(httpx.Client, "get", projection_get)
batch = plan_identifier_migration_batch(
plan,
repo_slugs=["one"],
projection_api_bases=["http://hub.test"],
)
assert batch["ok"] is False
assert batch["ready_for_approval"] is False
assert batch["repositories"][0]["projection_preflight"]["ok"] is False
assert any("current UUID=200" in error["reason"] for error in batch["errors"])
def test_migration_batch_plan_rejects_dirty_or_duplicate_scope(tmp_path: Path) -> None:
repo = tmp_path / "one"

View file

@ -294,6 +294,54 @@ def test_repairs_an_already_identified_workplan_projection(tmp_path: Path, monke
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"