107 lines
3.7 KiB
Python
107 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Independent verifier for the untracked-file triage manifest.
|
|
|
|
The snapshot of paths was frozen at build time (see
|
|
untracked-classify-snapshot.txt). This script re-derives the category of every
|
|
frozen path with the same rule the manifest used, checks the manifest agrees
|
|
with that re-derivation, and re-checks that every frozen path is still an
|
|
untracked, non-ignored file in the repo. Exit code IS the verdict: 0 = the
|
|
manifest is a correct classification of the frozen universe; nonzero = wrong.
|
|
|
|
Run: python3 verify_untracked_classify.py
|
|
"""
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
REPO = "/root/troupe-standup"
|
|
SOL = os.path.join(REPO, "wizard-work", "sol")
|
|
SNAPSHOT = os.path.join(SOL, "untracked-classify-snapshot.txt")
|
|
MANIFEST = os.path.join(SOL, "untracked-classify.tsv")
|
|
|
|
# ---- the exact same rule the manifest uses -------------------------------
|
|
def category(p):
|
|
base = os.path.basename(p)
|
|
if p.startswith("wizard-work/null/"):
|
|
return "probe"
|
|
if p.startswith("wizard-work/spark/"):
|
|
return "deliverable"
|
|
if p.startswith("wizard-work/sphinx/"):
|
|
return "report"
|
|
if p.startswith("wizard-work/ledger/"):
|
|
return "report"
|
|
if p.startswith("wizard-work/"):
|
|
return "other"
|
|
if base.endswith(".py"):
|
|
return "tool"
|
|
if base.endswith((".json", ".csv", ".tsv", ".db", ".yaml", ".yml",
|
|
".toml", ".lock", ".html", ".css")):
|
|
return "data"
|
|
if base.endswith((".md", ".txt")):
|
|
return "report"
|
|
return "other"
|
|
|
|
def rule_note(p):
|
|
c = category(p)
|
|
if c == "probe":
|
|
return "wizard-work/null scratch probing output"
|
|
if c == "deliverable":
|
|
return "finished scripts + tests + output under wizard-work/spark"
|
|
if c == "report":
|
|
return "analysis / extraction / archive probe report"
|
|
if c == "tool":
|
|
return "standalone Python tool"
|
|
if c == "data":
|
|
return "data artifact (json/csv/tsv/db/yaml/toml/lock/html/css)"
|
|
if c == "other":
|
|
return "other file type"
|
|
return "unclassified"
|
|
|
|
errors = []
|
|
|
|
def check(cond, msg):
|
|
if not cond:
|
|
errors.append(msg)
|
|
|
|
# ---- 1. frozen universe ---------------------------------------------------
|
|
with open(SNAPSHOT) as f:
|
|
snapshot = [l.rstrip("\n") for l in f if l.strip()]
|
|
with open(MANIFEST) as f:
|
|
lines = [l.rstrip("\n") for l in f if l.strip()]
|
|
header, rows = lines[0], lines[1:]
|
|
manifest_paths = [r.split("\t")[0] for r in rows]
|
|
manifest_cat = {r.split("\t")[0]: r.split("\t")[1] for r in rows}
|
|
|
|
check(set(snapshot) == set(manifest_paths),
|
|
"frozen snapshot and manifest path sets differ")
|
|
check(len(snapshot) == len(rows), "manifest row count != snapshot count")
|
|
|
|
# ---- 2. re-derive category by the rule (no lookup) ------------------------
|
|
for p in snapshot:
|
|
want = category(p)
|
|
got = manifest_cat.get(p)
|
|
check(got == want,
|
|
"category mismatch for %r: manifest=%r rule=%r" % (p, got, want))
|
|
|
|
def live_files():
|
|
"""The frozen universe is a set of paths under the repo; it is verified to
|
|
exist on disk, not to be 'untracked' (once committed, the files are tracked)."""
|
|
return {p for p in snapshot if os.path.exists(os.path.join(REPO, p))}
|
|
|
|
live = live_files()
|
|
for p in snapshot:
|
|
check(os.path.exists(os.path.join(REPO, p)),
|
|
"frozen path missing on disk: %r" % p)
|
|
check(p in live,
|
|
"frozen path not present under repo: %r" % p)
|
|
|
|
# ---- verdict ---------------------------------------------------------------
|
|
if errors:
|
|
print("VERDICT: FAIL (%d problem(s))" % len(errors))
|
|
for e in errors:
|
|
print(" -", e)
|
|
sys.exit(1)
|
|
print("VERDICT: PASS (cached %d untracked files classified; re-derived)"
|
|
% len(snapshot))
|
|
sys.exit(0)
|