#!/usr/bin/env python3 """Registry health: stale & unowned surface detection (ATLAS-WP-0003-T05). Reports promoted surfaces that need attention: - unowned : missing owner, or owner not resolvable to a known identity - stale : evidence.last_seen older than --max-age-days (default 180), or absent Ownership resolution uses reuse-surface's local-repo-roster as a stand-in for domain-tree identities (until domain-tree binding is wired, ATLAS-WP-0002 follow-up). Usage: python3 tools/registry_health.py [--max-age-days N] [--strict] make registry-health Exit 0 normally; 1 when --strict and any issue is found. """ from __future__ import annotations import datetime as _dt import sys from pathlib import Path try: import yaml except ImportError as exc: # pragma: no cover raise SystemExit(f"setup error: missing PyYAML ({exc})") from effective_config import SURFACES_DIR, load_entry ROSTER = Path.home() / "reuse-surface" / "registry" / "federation" / "local-repo-roster.yaml" def known_owners() -> set[str]: owners = {"custodian"} # State Hub domain identity not in the repo roster if ROSTER.exists(): data = yaml.safe_load(ROSTER.read_text()) or {} owners |= {r.get("slug") for r in data.get("repos", []) if r.get("slug")} return owners def main(argv: list[str]) -> int: max_age = 180 strict = "--strict" in argv if "--max-age-days" in argv: i = argv.index("--max-age-days") max_age = int(argv[i + 1]) cutoff = _dt.date.today() - _dt.timedelta(days=max_age) owners = known_owners() unowned: list[str] = [] stale: list[str] = [] for p in sorted(SURFACES_DIR.glob("*.md")): e = load_entry(p) sid = e.get("id", p.stem) owner = e.get("owner") if not owner: unowned.append(f"{sid}: missing owner") elif owner not in owners: unowned.append(f"{sid}: owner '{owner}' not resolvable to a known identity") seen = (e.get("evidence", {}) or {}).get("last_seen") if not seen: stale.append(f"{sid}: no evidence.last_seen") else: try: if _dt.date.fromisoformat(str(seen)) < cutoff: stale.append(f"{sid}: last_seen {seen} older than {max_age}d") except ValueError: stale.append(f"{sid}: unparseable last_seen '{seen}'") total = len(list(SURFACES_DIR.glob("*.md"))) print(f"registry health: {total} promoted surface(s)") print(f" unowned/unresolved: {len(unowned)}") for u in unowned: print(f" - {u}") print(f" stale (> {max_age}d): {len(stale)}") for s in stale: print(f" - {s}") if not unowned and not stale: print(" all surfaces owned and fresh.") return 1 if (strict and (unowned or stale)) else 0 if __name__ == "__main__": sys.path.insert(0, str(Path(__file__).resolve().parent)) raise SystemExit(main(sys.argv[1:]))