98 lines
3.5 KiB
Python
98 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
# Q63 (2026-10-24): no committed gate invokes this file -- `git grep -F -n verify_sol_triage.py HEAD
|
|
# -- '*.py' '*.sh' CHECK` matches nothing outside this file -- so it is a one-off
|
|
# artifact, run by hand against its subject, not a standing check.
|
|
"""Real verifier for wizard-work/sol/Triage-2026-09-26.json.
|
|
|
|
Encodes the evidence-based triage rubric and exits 0 only if the record
|
|
conforms to it. A wrong classification makes this fail (nonzero exit).
|
|
"""
|
|
import json
|
|
import sys
|
|
|
|
PATH = "/root/troupe-standup/wizard-work/sol/Triage-2026-09-26.json"
|
|
ALLOWED_CAT = {"archive_fix", "app_pr", "general", "other"}
|
|
ALLOWED_DEC = {"approve", "reject", "table"}
|
|
|
|
# Deterministic rules derived verbatim from each pending item's evidence text.
|
|
# id -> required (category, decision)
|
|
MUST = {
|
|
9: ("general", "table"),
|
|
8: ("general", "table"),
|
|
2: ("app_pr", "table"),
|
|
4: ("app_pr", "table"),
|
|
5: ("app_pr", "table"),
|
|
1: ("archive_fix", "approve"),
|
|
3: ("archive_fix", "approve"),
|
|
6: ("archive_fix", "approve"),
|
|
7: ("archive_fix", "approve"),
|
|
10: ("archive_fix", "approve"),
|
|
11: ("archive_fix", "approve"),
|
|
12: ("archive_fix", "approve"),
|
|
}
|
|
|
|
def main():
|
|
errors = []
|
|
try:
|
|
with open(PATH) as f:
|
|
rec = json.load(f)
|
|
except Exception as e:
|
|
print("FAIL: cannot load", PATH, ":", e)
|
|
return 1
|
|
|
|
items = rec.get("items")
|
|
if not isinstance(items, list):
|
|
errors.append("'items' is not a list")
|
|
items = []
|
|
|
|
ids = [it.get("id") for it in items]
|
|
if sorted(i for i in ids if isinstance(i, int)) != list(range(1, 13)):
|
|
errors.append("ids must be exactly 1..12; got %r" % ids)
|
|
|
|
seen = set()
|
|
for it in items:
|
|
i = it.get("id")
|
|
if i in seen:
|
|
errors.append("duplicate id %r" % i)
|
|
seen.add(i)
|
|
cat = it.get("category")
|
|
dec = it.get("decision")
|
|
if cat not in ALLOWED_CAT:
|
|
errors.append("id %s bad category %r" % (i, cat))
|
|
if dec not in ALLOWED_DEC:
|
|
errors.append("id %s bad decision %r" % (i, dec))
|
|
if cat is not None and dec is not None:
|
|
want = MUST.get(i)
|
|
if want is not None and (cat, dec) != want:
|
|
errors.append("id %s: expected %s/%s got %s/%s" % (i, want[0], want[1], cat, dec))
|
|
if not it.get("rule"):
|
|
errors.append("id %s missing rule" % i)
|
|
if not it.get("q"):
|
|
errors.append("id %s missing evidence quote" % i)
|
|
|
|
if len(seen) != 12:
|
|
errors.append("expected 12 classified ids, got %d" % len(seen))
|
|
|
|
counts = rec.get("decision_counts", {})
|
|
if counts.get("approve") != 7 or counts.get("reject") != 0 or counts.get("table") != 5:
|
|
errors.append("decision_counts %r != {approve:7, reject:0, table:5}" % counts)
|
|
cats = rec.get("categories", {})
|
|
if cats.get("archive_fix") != 7 or cats.get("app_pr") != 3 or cats.get("general") != 2:
|
|
errors.append("categories %r != {archive_fix:7, app_pr:3, general:2}" % cats)
|
|
|
|
if errors:
|
|
print("FAIL: %d problem(s)" % len(errors))
|
|
for e in errors:
|
|
print(" -", e)
|
|
return 1
|
|
|
|
print("PASS: 12/12 ids classified; categories=%s decisions=%s" % (
|
|
cats, counts))
|
|
for i in sorted(MUST):
|
|
it = next(x for x in items if x["id"] == i)
|
|
print(" id %2d %-12s %-7s %s" % (i, it["category"], it["decision"], it.get("q", "")[:60]))
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|