#!/usr/bin/env python3 """Re-verification test for the ledger repo-extraction artifact. Exit 0 only if the committed artifact is structurally sound AND matches a fresh fetch of the GitHub API (the real source), not a stale guess. """ import csv import json import sys import urllib.parse import urllib.request from datetime import datetime, timezone REPO_DIR = sys.argv[1] if len(sys.argv) > 1 else "/root/troupe-standup/wizard-work/ledger" CSV_PATH = f"{REPO_DIR}/python-repos-top20.csv" JSON_PATH = f"{REPO_DIR}/python-repos-top20.json" def fetch(url): req = urllib.request.Request(url, headers={"User-Agent": "ledger-wizard"}) with urllib.request.urlopen(req, timeout=60) as r: return json.loads(r.read().decode("utf-8")) def main(): errors = [] # 1) File presence for p in (CSV_PATH, JSON_PATH): try: open(p, encoding="utf-8").close() except FileNotFoundError: errors.append(f"missing {p}") if errors: print("FAIL: " + "; ".join(errors)) return 1 # 2) JSON shape with open(JSON_PATH, encoding="utf-8") as f: payload = json.load(f) rows = payload.get("rows") if not isinstance(rows, list) or len(rows) != 20: errors.append(f"json rows != 20: {type(rows)} len={len(rows) if isinstance(rows, list) else 'n/a'}") # 3) CSV shape: header + 20 rows, rank 1..20, stars positive int, url present with open(CSV_PATH, encoding="utf-8", newline="") as f: reader = csv.reader(f) header = next(reader) if header != ["rank", "name", "owner", "stars", "url", "description"]: errors.append(f"csv header {header}") csv_rows = list(reader) if len(csv_rows) != 20: errors.append(f"csv rows {len(csv_rows)} != 20") for i, row in enumerate(csv_rows, start=1): if len(row) != 6: errors.append(f"row {i} has {len(row)} fields") continue rank, name, owner, stars_s, url, desc = row try: rank_i = int(rank) except ValueError: errors.append(f"row {i} rank not int: {rank!r}") rank_i = None if rank_i != i: errors.append(f"row {i} rank {rank_i} != sequence {i}") if not stars_s.isdigit() or int(stars_s) <= 0: errors.append(f"row {i} stars not positive int: {stars_s!r}") if not url.startswith("https://github.com/"): errors.append(f"row {i} url malformed: {url!r}") # 4) Cross-check vs fresh API fetch (the actual evidence source) try: q = urllib.parse.quote("language:python") url = f"https://api.github.com/search/repositories?q={q}&sort=stars&order=desc&per_page=20" data = fetch(url) if data.get("incomplete_results"): errors.append("api returned incomplete_results=true") api_rows = [ {"full_name": it["full_name"], "stars": it["stargazers_count"]} for it in data.get("items", []) ] file_rows = [(r["full_name"], r["stars"]) for r in rows] if api_rows != file_rows: errors.append(f"api mismatch: {len(api_rows)} api rows vs {len(file_rows)} file rows") for a, b in zip(api_rows, file_rows): if a != b: errors.append(f" first mismatch: api={a} file={b}") break except Exception as exc: # noqa: BLE001 errors.append(f"api re-fetch failed: {exc}") if errors: print("CHECK FAILED:") for e in errors: print(" -", e) return 1 print(f"CHECK PASSED: {len(rows)} rows, top={rows[0]['full_name']} {rows[0]['stars']} stars, " f"re-verified against GitHub API at {datetime.now(timezone.utc).isoformat()}") return 0 if __name__ == "__main__": sys.exit(main())