feat(projection): derive a repository's projection from the forge
Implements ADR-012 decisions 1 and 2 (STATE-WP-0083 T01, T02 partial). Central clones the default branch from Forgejo and derives its own projection: 69 workplans and 459 tasks from the-custodian at d5013ae, identical across runs, with the commit recorded as provenance. Identifiers are derived in the ADR-007 namespace and verified against live records, so a forge-derived projection and a preliminary overlay agree on identity without reconciliation. The diff first matched hub records by UUID and was badly wrong: most hub records carry pre-ADR-007 random identifiers, so nearly everything appeared simultaneously missing and stale, and a reset built on it would have destroyed and recreated the entire projection. It now matches canonical record id, falling back to the backing file. whitehat-security — bootstrapped straight from files — now reports clean, which is the control. Task-level comparison is deliberately not trusted: hub tasks carry no canonical record id, only a title, so matching is by title. Recorded as T06; T03 is limited to workplans until it lands. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 2583210@bnt-lap001 Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
This commit is contained in:
parent
6390b7bead
commit
fd0d0d537b
3 changed files with 550 additions and 2 deletions
98
tests/test_forge_projection.py
Normal file
98
tests/test_forge_projection.py
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
"""Deriving a projection from the forge (STATE-WP-0083-T01).
|
||||
|
||||
The properties that matter are identity and determinism: a forge-derived
|
||||
projection must compute the same record identities as repo-manager, and the same
|
||||
commit must always yield the same projection. Without both, the reset in T03
|
||||
cannot be verified against anything.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from api.services import forge_projection as fp
|
||||
|
||||
|
||||
def test_identity_matches_the_fleet_derivation():
|
||||
"""Same namespace as ADR-007, so overlay and forge agree on identity."""
|
||||
assert fp.derived_record_uuid("CUST-WP-0067") == "16249302-2767-55df-aec0-d92c2751c225"
|
||||
assert fp.derived_record_uuid("CUST-WP-0067-T01") == "f3608db4-20a5-58fb-a965-885eb14858af"
|
||||
|
||||
|
||||
def _repo(tmp_path: Path) -> Path:
|
||||
root = tmp_path / "demo"
|
||||
(root / "workplans" / "archived").mkdir(parents=True)
|
||||
(root / "workplans" / "DEMO-WP-0001-a.md").write_text(
|
||||
"---\nid: DEMO-WP-0001\ntype: workplan\ntitle: \"First\"\nstatus: active\n---\n\n"
|
||||
"## Do the thing\n\n```task\nid: DEMO-WP-0001-T01\nstatus: todo\npriority: high\n```\n\n"
|
||||
"## Do the other\n\n```task\nid: DEMO-WP-0001-T02\nstatus: done\npriority: low\n```\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "workplans" / "archived" / "260101-DEMO-WP-0002-b.md").write_text(
|
||||
"---\nid: DEMO-WP-0002\ntype: workplan\ntitle: \"Second\"\nstatus: finished\n---\n\n# b\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "workplans" / "NOTES.md").write_text(
|
||||
"---\nid: NOT-A-WORKPLAN\ntype: note\n---\n\n# not a workplan\n", encoding="utf-8"
|
||||
)
|
||||
return root
|
||||
|
||||
|
||||
def test_derives_workplans_tasks_and_archived_flag(tmp_path):
|
||||
p = fp.derive_from_checkout(_repo(tmp_path), "demo", "abc123")
|
||||
assert [w.record_id for w in p.workplans] == ["DEMO-WP-0001", "DEMO-WP-0002"]
|
||||
first, second = p.workplans
|
||||
assert first.status == "active" and first.archived is False
|
||||
assert second.archived is True
|
||||
assert p.task_count == 2
|
||||
assert [t.record_id for t in first.tasks] == ["DEMO-WP-0001-T01", "DEMO-WP-0001-T02"]
|
||||
|
||||
|
||||
def test_ignores_files_that_are_not_workplans(tmp_path):
|
||||
"""Selection is by `type: workplan`; anything else is not this hub's business."""
|
||||
p = fp.derive_from_checkout(_repo(tmp_path), "demo", "abc123")
|
||||
assert all(w.record_id != "NOT-A-WORKPLAN" for w in p.workplans)
|
||||
|
||||
|
||||
def test_task_titles_fall_back_to_the_preceding_heading(tmp_path):
|
||||
p = fp.derive_from_checkout(_repo(tmp_path), "demo", "abc123")
|
||||
titles = [t.title for t in p.workplans[0].tasks]
|
||||
assert titles == ["Do the thing", "Do the other"]
|
||||
|
||||
|
||||
def test_identifiers_are_derived_not_read_from_the_file(tmp_path):
|
||||
"""A forge projection must not inherit whatever id a file happens to carry."""
|
||||
root = _repo(tmp_path)
|
||||
f = root / "workplans" / "DEMO-WP-0001-a.md"
|
||||
f.write_text(
|
||||
f.read_text(encoding="utf-8").replace(
|
||||
"status: active",
|
||||
'status: active\nstate_hub_workstream_id: "00000000-0000-4000-8000-000000000000"',
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
p = fp.derive_from_checkout(root, "demo", "abc123")
|
||||
assert p.workplans[0].uuid == fp.derived_record_uuid("DEMO-WP-0001")
|
||||
assert p.workplans[0].uuid != "00000000-0000-4000-8000-000000000000"
|
||||
|
||||
|
||||
def test_same_input_yields_identical_output(tmp_path):
|
||||
root = _repo(tmp_path)
|
||||
assert fp.derive_from_checkout(root, "demo", "abc").to_dict() == \
|
||||
fp.derive_from_checkout(root, "demo", "abc").to_dict()
|
||||
|
||||
|
||||
def test_missing_workplans_directory_is_empty_not_an_error(tmp_path):
|
||||
(tmp_path / "bare").mkdir()
|
||||
p = fp.derive_from_checkout(tmp_path / "bare", "bare", "abc")
|
||||
assert p.workplans == [] and p.commit == "abc"
|
||||
|
||||
|
||||
def test_clone_failure_is_reported_not_swallowed(monkeypatch):
|
||||
def boom(*a, **k):
|
||||
raise fp.ForgeDeriveError("repository not found")
|
||||
monkeypatch.setattr(fp, "_run_git", boom)
|
||||
with pytest.raises(fp.ForgeDeriveError, match="not found"):
|
||||
fp.derive_from_forge("nope")
|
||||
Loading…
Add table
Add a link
Reference in a new issue