feat: generate repository rename adoption plans
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a049a4-ee9f-78e1-9d66-2cb0f9bea3e3
This commit is contained in:
parent
bdf3a1fdf8
commit
ef0b6df3b8
7 changed files with 1294 additions and 5 deletions
|
|
@ -388,3 +388,86 @@ def test_human_status_distinguishes_lifecycle_states(
|
|||
_run(monkeypatch, "repo", "rename", "status", OPERATION_ID)
|
||||
captured = capsys.readouterr()
|
||||
assert f"{label}: repository rename status" in captured.out + captured.err
|
||||
|
||||
|
||||
def test_generate_workplan_cli_writes_once_and_reports_contract(
|
||||
monkeypatch, tmp_path, capsys
|
||||
):
|
||||
repo_root = tmp_path / "flex-auth"
|
||||
repo_root.mkdir()
|
||||
(repo_root / "AGENTS.md").write_text(
|
||||
"**Workplan prefix:** `FLEX-WP-`\n"
|
||||
)
|
||||
output = repo_root / "workplans" / "rename.md"
|
||||
report = {
|
||||
"schema_version": "state-hub.repository-rename-preflight.v1",
|
||||
"repo_id": REPO_ID,
|
||||
"old_slug": "flex-auth",
|
||||
"new_slug": "access-engine",
|
||||
"safe_to_apply": True,
|
||||
"blockers": [],
|
||||
"warnings": [],
|
||||
"report_checksum": "a" * 64,
|
||||
"preflight_token": "private-token",
|
||||
"preflighted_at": "2026-08-29T08:00:00Z",
|
||||
"current": {
|
||||
"statehub": {"local_path": str(repo_root), "host_paths": {}},
|
||||
"forge_identity": {
|
||||
"verification_state": "verified",
|
||||
"forge_repository_id": 42,
|
||||
},
|
||||
"forge_available": True,
|
||||
"forge": {
|
||||
"repository_id": 42,
|
||||
"default_branch": "main",
|
||||
"head_commit": "b" * 40,
|
||||
},
|
||||
},
|
||||
"active_work": {"workplans": [], "tasks": []},
|
||||
"affected": {"external_handoffs": []},
|
||||
}
|
||||
repo = {**_repo(), "local_path": str(repo_root), "host_paths": {}}
|
||||
workplans = [
|
||||
{
|
||||
"slug": "flex-wp-0018",
|
||||
"backing_filename": "FLEX-WP-0018-last.md",
|
||||
"status": "finished",
|
||||
}
|
||||
]
|
||||
|
||||
def request(_api_base, method, path, body=None, *, expected=dict):
|
||||
del body
|
||||
if path.startswith("/repos/flex-auth"):
|
||||
return repo
|
||||
if path.startswith("/workplans/"):
|
||||
assert expected is list
|
||||
return workplans
|
||||
assert method == "POST"
|
||||
return report
|
||||
|
||||
monkeypatch.setattr(rename_cli, "_api_request", request)
|
||||
args = (
|
||||
"repo",
|
||||
"rename",
|
||||
"generate-workplan",
|
||||
"flex-auth",
|
||||
"access-engine",
|
||||
"--repo-path",
|
||||
str(repo_root),
|
||||
"--output",
|
||||
str(output),
|
||||
"--json",
|
||||
)
|
||||
_run(monkeypatch, *args)
|
||||
result = json.loads(capsys.readouterr().out)
|
||||
assert result["state"] == "achieved"
|
||||
assert result["result"]["workplan_id"] == "FLEX-WP-0019"
|
||||
assert result["result"]["workplan_prefix"] == "FLEX-WP"
|
||||
assert result["result"]["state_hub_uuid_fields_written"] == []
|
||||
assert "private-token" not in output.read_text()
|
||||
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
_run(monkeypatch, *args)
|
||||
assert exc.value.code == 1
|
||||
refused = json.loads(capsys.readouterr().out)
|
||||
assert refused["error"]["code"] == "output_exists"
|
||||
|
|
|
|||
323
tests/test_repository_rename_workplan.py
Normal file
323
tests/test_repository_rename_workplan.py
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from repository_rename_workplan import (
|
||||
WorkplanGenerationError,
|
||||
build_generation_snapshot,
|
||||
content_checksum,
|
||||
default_output_path,
|
||||
render_workplan,
|
||||
write_workplan_exclusive,
|
||||
)
|
||||
from scripts.quality_debt import collect as collect_quality_debt
|
||||
from scripts.validate_repo_adr import validate
|
||||
|
||||
|
||||
REPO_ID = "fda8ad85-a7d7-4055-8f21-902a533e59df"
|
||||
|
||||
|
||||
def _repo(path: str | None = None, **extra) -> dict:
|
||||
return {
|
||||
"id": REPO_ID,
|
||||
"slug": "flex-auth",
|
||||
"canonical_slug": "flex-auth",
|
||||
"domain_slug": "infotech",
|
||||
"local_path": path,
|
||||
"host_paths": {},
|
||||
"aliases": [],
|
||||
**extra,
|
||||
}
|
||||
|
||||
|
||||
def _preflight(**extra) -> dict:
|
||||
report = {
|
||||
"schema_version": "state-hub.repository-rename-preflight.v1",
|
||||
"repo_id": REPO_ID,
|
||||
"old_slug": "flex-auth",
|
||||
"new_slug": "access-engine",
|
||||
"safe_to_apply": True,
|
||||
"blockers": [],
|
||||
"warnings": [],
|
||||
"report_checksum": "a" * 64,
|
||||
"preflight_token": "must-never-appear-in-workplan",
|
||||
"preflighted_at": "2026-08-29T08:00:00Z",
|
||||
"current": {
|
||||
"statehub": {
|
||||
"repo_id": REPO_ID,
|
||||
"local_path": None,
|
||||
"host_paths": {},
|
||||
},
|
||||
"forge_identity": {
|
||||
"verification_state": "verified",
|
||||
"forge_repository_id": 42,
|
||||
"forge_owner": "coulomb",
|
||||
},
|
||||
"forge_available": True,
|
||||
"forge": {
|
||||
"repository_id": 42,
|
||||
"default_branch": "main",
|
||||
"head_commit": "b" * 40,
|
||||
},
|
||||
},
|
||||
"active_work": {"workplans": [], "tasks": []},
|
||||
"affected": {"external_handoffs": []},
|
||||
}
|
||||
report.update(extra)
|
||||
return report
|
||||
|
||||
|
||||
def _workplans() -> list[dict]:
|
||||
return [
|
||||
{
|
||||
"id": "11111111-1111-4111-8111-111111111111",
|
||||
"slug": "flex-wp-0017",
|
||||
"status": "active",
|
||||
"backing_filename": "FLEX-WP-0017-action-contract.md",
|
||||
},
|
||||
{
|
||||
"id": "22222222-2222-4222-8222-222222222222",
|
||||
"slug": "flex-wp-0018",
|
||||
"status": "archived",
|
||||
"backing_filename": "260828-FLEX-WP-0018-inbound-corrections.md",
|
||||
"backing_archived": True,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _repo_root(tmp_path: Path) -> Path:
|
||||
root = tmp_path / "flex-auth"
|
||||
root.mkdir()
|
||||
(root / "AGENTS.md").write_text("**Workplan prefix:** `FLEX-WP-`\n")
|
||||
return root
|
||||
|
||||
|
||||
def test_flex_auth_snapshot_is_deterministic_and_complete(tmp_path):
|
||||
root = _repo_root(tmp_path)
|
||||
report = _preflight(
|
||||
warnings=[
|
||||
{
|
||||
"code": "active_work_present",
|
||||
"message": "coordinate NetKingdom security-stack work",
|
||||
"workplan_count": 1,
|
||||
}
|
||||
],
|
||||
affected={
|
||||
"external_handoffs": [
|
||||
"fabric-graph-projections",
|
||||
"interface-change-consumers",
|
||||
]
|
||||
},
|
||||
active_work={
|
||||
"workplans": [
|
||||
{
|
||||
"id": "33333333-3333-4333-8333-333333333333",
|
||||
"slug": "FLEX-WP-0017",
|
||||
"status": "active",
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "44444444-4444-4444-8444-444444444444",
|
||||
"record_id": "FLEX-WP-0017-T05",
|
||||
"status": "progress",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
snapshot = build_generation_snapshot(
|
||||
repo=_repo(str(root), aliases=["flexauth-legacy"]),
|
||||
preflight=report,
|
||||
workplans=_workplans(),
|
||||
)
|
||||
first = render_workplan(snapshot, owner="codex")
|
||||
second = render_workplan(snapshot, owner="codex")
|
||||
|
||||
assert first == second
|
||||
assert content_checksum(first) == hashlib.sha256(first.encode()).hexdigest()
|
||||
assert snapshot.prefix == "FLEX-WP"
|
||||
assert snapshot.workplan_id == "FLEX-WP-0019"
|
||||
assert first.count("```task\n") == 11
|
||||
assert "id: FLEX-WP-0019-T06\nstatus: wait" in first
|
||||
assert "id: FLEX-WP-0019-T11\nstatus: wait" in first
|
||||
assert "state_hub_workstream_id:" not in first
|
||||
assert "state_hub_task_id:" not in first
|
||||
assert "must-never-appear-in-workplan" not in first
|
||||
assert "`active_work_present`" in first
|
||||
assert "`fabric-graph-projections`" in first
|
||||
assert "`interface-change-consumers`" in first
|
||||
assert "FLEX_AUTH_*" in first
|
||||
assert "NetKingdom policy consumers" in first
|
||||
assert "flexauth-legacy" in first
|
||||
assert "| Credential-route catalog | `ops-warden` |" in first
|
||||
assert "only that repository closes" in first
|
||||
|
||||
|
||||
def test_generated_snapshot_passes_parser_and_quality_debt(tmp_path):
|
||||
root = _repo_root(tmp_path)
|
||||
snapshot = build_generation_snapshot(
|
||||
repo=_repo(str(root)),
|
||||
preflight=_preflight(),
|
||||
workplans=_workplans(),
|
||||
)
|
||||
content = render_workplan(snapshot, owner="codex")
|
||||
output = default_output_path(snapshot)
|
||||
write_workplan_exclusive(output, content)
|
||||
|
||||
report = validate(root, skip_api=True)
|
||||
assert report.failures == []
|
||||
assert collect_quality_debt(
|
||||
root, api_base=None, include_hub_intakes=False
|
||||
) == []
|
||||
assert output.name.startswith("FLEX-WP-0019-")
|
||||
|
||||
|
||||
def test_snapshot_counts_archived_workplans_when_allocating_id(tmp_path):
|
||||
root = _repo_root(tmp_path)
|
||||
snapshot = build_generation_snapshot(
|
||||
repo=_repo(str(root)),
|
||||
preflight=_preflight(),
|
||||
workplans=[
|
||||
{
|
||||
"slug": "flex-wp-0004",
|
||||
"status": "active",
|
||||
"backing_filename": "FLEX-WP-0004-live.md",
|
||||
},
|
||||
{
|
||||
"slug": "flex-wp-0099",
|
||||
"status": "archived",
|
||||
"backing_filename": "260101-FLEX-WP-0099-old.md",
|
||||
"backing_archived": True,
|
||||
},
|
||||
],
|
||||
)
|
||||
assert snapshot.workplan_id == "FLEX-WP-0100"
|
||||
assert snapshot.archived_workplan_ids == ("FLEX-WP-0099",)
|
||||
|
||||
|
||||
def test_snapshot_renders_active_work_and_private_forge_failure(tmp_path):
|
||||
root = _repo_root(tmp_path)
|
||||
report = _preflight(
|
||||
safe_to_apply=False,
|
||||
blockers=[
|
||||
{
|
||||
"code": "forge_unreadable",
|
||||
"message": "private repository could not be inspected",
|
||||
}
|
||||
],
|
||||
current={
|
||||
"statehub": {"repo_id": REPO_ID, "host_paths": {}},
|
||||
"forge_identity": {"verification_state": "unverified"},
|
||||
"forge_available": False,
|
||||
"forge": None,
|
||||
},
|
||||
active_work={
|
||||
"workplans": [
|
||||
{"id": "active-uuid", "slug": "FLEX-WP-0017", "status": "active"}
|
||||
],
|
||||
"tasks": [],
|
||||
},
|
||||
)
|
||||
snapshot = build_generation_snapshot(
|
||||
repo=_repo(str(root)), preflight=report, workplans=_workplans()
|
||||
)
|
||||
rendered = render_workplan(snapshot, owner="codex")
|
||||
assert "status: proposed" in rendered
|
||||
assert "`forge_unreadable`" in rendered
|
||||
assert "`forge-identity-unverified`" in rendered
|
||||
assert "`forge-repository-unreadable`" in rendered
|
||||
assert "workplan `FLEX-WP-0017` / `active-uuid` is `active`" in rendered
|
||||
assert "id: FLEX-WP-0019-T06\nstatus: wait" in rendered
|
||||
|
||||
|
||||
def test_snapshot_supports_missing_local_path_from_indexed_workplans(tmp_path):
|
||||
missing = tmp_path / "not-mounted"
|
||||
snapshot = build_generation_snapshot(
|
||||
repo=_repo(str(missing)),
|
||||
preflight=_preflight(),
|
||||
workplans=_workplans(),
|
||||
)
|
||||
rendered = render_workplan(snapshot, owner="codex")
|
||||
assert snapshot.prefix == "FLEX-WP"
|
||||
assert snapshot.path_available is False
|
||||
assert "`local-path-unavailable`" in rendered
|
||||
with pytest.raises(WorkplanGenerationError) as exc:
|
||||
default_output_path(snapshot)
|
||||
assert exc.value.code == "repository_path_unavailable"
|
||||
|
||||
|
||||
def test_snapshot_preserves_old_alias_context(tmp_path):
|
||||
root = _repo_root(tmp_path)
|
||||
snapshot = build_generation_snapshot(
|
||||
repo=_repo(str(root), aliases=["old-flex", "flex-auth"]),
|
||||
preflight=_preflight(),
|
||||
workplans=_workplans(),
|
||||
)
|
||||
rendered = render_workplan(snapshot, owner="codex")
|
||||
assert 'protected/current aliases: `["flex-auth", "old-flex"]`' in rendered
|
||||
assert "Retain the protected `flex-auth` alias" in rendered
|
||||
|
||||
|
||||
def test_existing_uuid_fields_are_untouched_and_not_copied_to_new_plan(tmp_path):
|
||||
root = _repo_root(tmp_path)
|
||||
workplans = root / "workplans"
|
||||
workplans.mkdir()
|
||||
existing = workplans / "FLEX-WP-0001-existing.md"
|
||||
original = """---
|
||||
id: FLEX-WP-0001
|
||||
type: workplan
|
||||
title: Existing
|
||||
domain: infotech
|
||||
status: active
|
||||
owner: codex
|
||||
created: "2026-01-01"
|
||||
state_hub_workstream_id: "55555555-5555-4555-8555-555555555555"
|
||||
---
|
||||
"""
|
||||
existing.write_text(original)
|
||||
snapshot = build_generation_snapshot(
|
||||
repo=_repo(str(root)), preflight=_preflight(), workplans=[]
|
||||
)
|
||||
rendered = render_workplan(snapshot, owner="codex")
|
||||
assert existing.read_text() == original
|
||||
assert "55555555-5555-4555-8555-555555555555" not in rendered
|
||||
assert "state_hub_workstream_id:" not in rendered
|
||||
|
||||
|
||||
def test_exclusive_writer_refuses_existing_output(tmp_path):
|
||||
output = tmp_path / "existing.md"
|
||||
output.write_text("operator content\n")
|
||||
with pytest.raises(WorkplanGenerationError) as exc:
|
||||
write_workplan_exclusive(output, "replacement\n")
|
||||
assert exc.value.code == "output_exists"
|
||||
assert output.read_text() == "operator content\n"
|
||||
|
||||
|
||||
def test_prefix_is_never_derived_from_new_slug(tmp_path):
|
||||
root = tmp_path / "repo"
|
||||
root.mkdir()
|
||||
snapshot = build_generation_snapshot(
|
||||
repo=_repo(str(root)),
|
||||
preflight=_preflight(),
|
||||
workplans=_workplans(),
|
||||
)
|
||||
assert snapshot.prefix == "FLEX-WP"
|
||||
assert not snapshot.workplan_id.startswith("ACCESS-ENGINE")
|
||||
|
||||
|
||||
def test_ambiguous_indexed_prefixes_fail_closed(tmp_path):
|
||||
root = tmp_path / "repo"
|
||||
root.mkdir()
|
||||
with pytest.raises(WorkplanGenerationError) as exc:
|
||||
build_generation_snapshot(
|
||||
repo=_repo(str(root)),
|
||||
preflight=_preflight(),
|
||||
workplans=[
|
||||
{"slug": "flex-wp-0001"},
|
||||
{"slug": "other-wp-0001"},
|
||||
],
|
||||
)
|
||||
assert exc.value.code == "workplan_prefix_ambiguous"
|
||||
Loading…
Add table
Add a link
Reference in a new issue