Files
config-atlas/tools/registry_health.py
tegwick bc702db4cf
Some checks failed
validate-registry / validate (push) Has been cancelled
feat(connectors): complete ATLAS-WP-0003 — discovery connectors (Phase 2)
T01 connector_base + docs/discovery-connectors.md (read-only/stateless,
candidate->PR->promote; `candidate` added to schema status enum; candidates/
gitignored, excluded from gate).
T02 connector_reposcoping (repo-scoping facts -> candidates; graceful degrade).
T03 connector_gitconfig (deterministic scan; real .env -> secret-ref, no values;
verified 4 real candidates from ~/state-hub).
T04 connector_featurecontrol (feature-flag surfaces linking to feature-control
keys, no eval logic; FR-12).
T05 registry_health (unowned + stale detection).
Make targets: connect-gitconfig/reposcoping/featurecontrol, registry-health.

WP-0003 finished (5/5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 00:27:57 +02:00

85 lines
2.9 KiB
Python

#!/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:]))