RMGR-WP-0019-T03: add read-only retirement disposition check
Re-ran the registrar suite on current source and pinned the three empty duplicate Activity projections the receiving run refused, so the Activity disposition can be verified before STATE-WP-0095-T04 applies it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 57383@bnt-lap001 Assistant-Session: ef71febe-2a37-4c34-a979-2508ca973c91
This commit is contained in:
parent
8a47a5c102
commit
65b7919ec7
3 changed files with 133 additions and 1 deletions
81
src/repo_manager/retirement_disposition.py
Normal file
81
src/repo_manager/retirement_disposition.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
"""Read-only check of an Activity Core retirement disposition (RMGR-WP-0019-T03).
|
||||
|
||||
The 2026-10-04 Activity receiving run refused exactly three empty duplicate
|
||||
projections. Before STATE-WP-0095-T04 may apply them, the owner's
|
||||
``expected_retirement_uuids`` must match what Repo Manager observed: the same
|
||||
three projections, none bound to tasks, and never the source-bound parent.
|
||||
This module only compares data; it performs no hub or source writes.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Projections the receiving run refused as "would be retired" (evidence file
|
||||
# docs/evidence/2026-10-04-historical-registrar-verification.json) with the
|
||||
# hub ids observed read-only on 2026-10-06.
|
||||
REFUSED_DUPLICATE_PROJECTIONS: dict[str, dict[str, str]] = {
|
||||
"683724d9-4249-5e0e-88e0-7baff4330d58": {
|
||||
"slug": "custodian-wp-0003",
|
||||
"backing_relative_path": "workplans/archived/260707-custodian-WP-0003-event-bridge.md",
|
||||
},
|
||||
"644017c7-c5f2-5a64-81c3-d8ee806f8251": {
|
||||
"slug": "custodian-wp-0003a",
|
||||
"backing_relative_path": "workplans/custodian-WP-0003a-event-bridge-model-rules-registry.md",
|
||||
},
|
||||
"a5806df9-5b9a-50da-81c1-7e606076bed9": {
|
||||
"slug": "custodian-wp-0003b",
|
||||
"backing_relative_path": "workplans/custodian-WP-0003b-event-bridge-parser-workflow-triggers.md",
|
||||
},
|
||||
}
|
||||
|
||||
# Source-bound parent every custodian-WP-0003* file names; it must survive.
|
||||
PRESERVED_PARENT_UUIDS = frozenset({"b4eb45a9-69e3-4ab0-b00c-67a53c3117c5"})
|
||||
|
||||
|
||||
def check_disposition(
|
||||
expected_retirement_uuids: list[str],
|
||||
task_counts: dict[str, int] | None = None,
|
||||
) -> list[str]:
|
||||
"""Return problems; an empty list means the disposition is consistent."""
|
||||
problems: list[str] = []
|
||||
given = [u.lower() for u in expected_retirement_uuids]
|
||||
if len(given) != len(set(given)):
|
||||
problems.append("duplicate UUIDs in expected_retirement_uuids")
|
||||
given_set = set(given)
|
||||
known = set(REFUSED_DUPLICATE_PROJECTIONS)
|
||||
for uuid in sorted(given_set & PRESERVED_PARENT_UUIDS):
|
||||
problems.append(f"{uuid} is the preserved source-bound parent")
|
||||
for uuid in sorted(given_set - known - PRESERVED_PARENT_UUIDS):
|
||||
problems.append(f"{uuid} was not refused by the receiving run")
|
||||
for uuid in sorted(known - given_set):
|
||||
slug = REFUSED_DUPLICATE_PROJECTIONS[uuid]["slug"]
|
||||
problems.append(f"{uuid} ({slug}) refused but missing from disposition")
|
||||
for uuid in sorted(given_set & known):
|
||||
count = (task_counts or {}).get(uuid)
|
||||
if count is None:
|
||||
problems.append(f"{uuid} task count not verified")
|
||||
elif count != 0:
|
||||
problems.append(f"{uuid} holds {count} tasks; not an empty projection")
|
||||
return problems
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
"""Usage: python -m repo_manager.retirement_disposition DISPOSITION.json
|
||||
|
||||
The JSON needs ``expected_retirement_uuids`` and may carry ``task_counts``.
|
||||
"""
|
||||
if len(argv) != 2:
|
||||
print(main.__doc__, file=sys.stderr)
|
||||
return 2
|
||||
data = json.loads(Path(argv[1]).read_text())
|
||||
problems = check_disposition(
|
||||
data.get("expected_retirement_uuids", []), data.get("task_counts")
|
||||
)
|
||||
print(json.dumps({"ok": not problems, "problems": problems}, indent=2))
|
||||
return 0 if not problems else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv))
|
||||
38
tests/test_retirement_disposition.py
Normal file
38
tests/test_retirement_disposition.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
from repo_manager.retirement_disposition import (
|
||||
PRESERVED_PARENT_UUIDS,
|
||||
REFUSED_DUPLICATE_PROJECTIONS,
|
||||
check_disposition,
|
||||
)
|
||||
|
||||
EXACT = sorted(REFUSED_DUPLICATE_PROJECTIONS)
|
||||
EMPTY = {u: 0 for u in EXACT}
|
||||
|
||||
|
||||
def test_exact_empty_set_is_consistent():
|
||||
assert check_disposition(EXACT, EMPTY) == []
|
||||
|
||||
|
||||
def test_missing_projection_is_reported():
|
||||
problems = check_disposition(EXACT[:2], EMPTY)
|
||||
assert any("missing from disposition" in p for p in problems)
|
||||
|
||||
|
||||
def test_preserved_parent_is_refused():
|
||||
parent = next(iter(PRESERVED_PARENT_UUIDS))
|
||||
problems = check_disposition(EXACT + [parent], EMPTY)
|
||||
assert any("preserved source-bound parent" in p for p in problems)
|
||||
|
||||
|
||||
def test_unknown_uuid_and_nonempty_projection_are_reported():
|
||||
counts = dict(EMPTY, **{EXACT[0]: 3})
|
||||
problems = check_disposition(EXACT + ["00000000-0000-0000-0000-000000000000"], counts)
|
||||
assert any("not refused" in p for p in problems)
|
||||
assert any("holds 3 tasks" in p for p in problems)
|
||||
|
||||
|
||||
def test_unverified_task_counts_are_not_assumed_empty():
|
||||
assert all("not verified" in p for p in check_disposition(EXACT))
|
||||
|
||||
|
||||
def test_duplicates_are_reported():
|
||||
assert "duplicate UUIDs" in check_disposition(EXACT + [EXACT[0].upper()], EMPTY)[0]
|
||||
|
|
@ -7,7 +7,7 @@ repo: repo-manager
|
|||
status: active
|
||||
owner: codex
|
||||
created: "2026-10-04"
|
||||
updated: "2026-10-04"
|
||||
updated: "2026-10-06"
|
||||
flavor: implementation
|
||||
depends_on: []
|
||||
state_hub_workstream_id: "efe0ecb2-2f3f-5f49-baee-b90ad1d353ab"
|
||||
|
|
@ -93,3 +93,16 @@ normal path; frozen historical source files stayed unchanged. T03 remains wait
|
|||
for explicit disposition and bounded receiving support under STATE-WP-0095-T04
|
||||
(decision91b7c633-a66f-5d43-9f4b-6565aefb9356).
|
||||
Evidence: docs/evidence/2026-10-04-historical-registrar-verification.json.
|
||||
|
||||
## Ungated T03 preparation, 2026-10-06
|
||||
|
||||
Re-ran the registrar suite at 8a47a5c (registrar source unchanged since
|
||||
c41d36c2): 89 focused registrar/identity/historical cases and the full suite
|
||||
(226) passed. Read-only hub check: the three refused projections are
|
||||
683724d9 (custodian-wp-0003), 644017c7 (custodian-wp-0003a) and a5806df9
|
||||
(custodian-wp-0003b), each with zero tasks; every custodian-WP-0003* source
|
||||
file binds parent b4eb45a9, which holds the tasks and must not be retired.
|
||||
Added repo_manager.retirement_disposition, a read-only check that an Activity
|
||||
expected_retirement_uuids set equals exactly those three empty projections and
|
||||
excludes the preserved parent. It authorizes nothing; retirement stays with
|
||||
Activity receiving under STATE-WP-0095-T04.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue