generated from coulomb/repo-seed
Some checks failed
validate-registry / validate (push) Has been cancelled
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>
85 lines
2.9 KiB
Python
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:]))
|