#!/usr/bin/env python3 """Q137 -- the work queue's declared open set, derived and then measured. Two questions, kept apart on purpose: DECLARED which ids does `WORK-QUEUE.md` itself leave open? That is a textual fact: the last declaration for an id, and whether that declaration's status word opens or closes it. `--check` pins the answer so the set cannot drift unnoticed. MEASURED for each declared-open id, is the defect its record describes still present at HEAD? Each probe is a named function that reads or runs the thing the record cites and returns a state plus the evidence it read. `--closures` pins those states. A repaired item with no closure record is the defect this file exists for: the queue says "OPEN", a newcomer takes it, and the work is already done. The probe table is the measurement; the pin is what makes the next run a regression test rather than a fresh opinion. States REPAIRED the cited defect is gone at HEAD, measured by the probe PARTIAL one part of the record is satisfied, another is not STILL-OPEN the cited defect is still there at HEAD UNMEASURED no probe here can settle it (network, archive, judgement) Exit codes (tools/ contract) 0 graded clean / pin matches 1 a fault: drift from the pin, or a probe moved 2 could not grade (missing file, unreadable queue, probe crashed) Stdlib only. Nothing here writes to the tree. """ import argparse import ast import importlib.util import io import json import os import re import shutil import subprocess import sys import tempfile from contextlib import redirect_stdout from pathlib import Path QUEUE = "WORK-QUEUE.md" # ---------------------------------------------------------------- declarations EM = "\u2014" SEP = re.compile(r"^[\u2014\u2013-]{1,2}\s*") DASHES = (" " + EM + " ", " -- ", " - ") OPEN_WORDS = {"OPEN", "OPENED", "REOPENED"} CLOSED_WORDS = {"DONE", "CLOSED", "VERIFIED", "REPAIRED", "ACCEPTED", "CORRECTION", "ERRATUM", "WITHDRAWN", "RETIRED", "ANSWERED", "RESOLVED"} CLOSED_IN_PLACE = re.compile(r"OPENED and REPAIRED|OPENED and CLOSED|" r"opened and repaired in the same tick") HEADING = re.compile(r"^(#{2,})\s*Q(\d+)\b([\s\u2014\u2013-].*)?$") RECORD = re.compile(r"^\*\*Q(\d+)\b\s*[\u2014\u2013-]{1,2}\s*(.*)$") def classify(tail): """State word from the text after the id, or 'OTHER' / 'SECTION'.""" t = tail.strip().lstrip("*").strip() t = SEP.sub("", t).strip() word = t.split()[0].strip(".,:;()") if t.split() else "" word = word.strip("*").strip(".,:;()") w = word.upper() if w in OPEN_WORDS: if CLOSED_IN_PLACE.search(t[:80]): return "CLOSED" return "OPEN" if w in CLOSED_WORDS: return "CLOSED" return "OTHER" def parse(text): """-> (decls, state): decls is a list of dicts in line order.""" decls = [] for n, line in enumerate(text.splitlines(), 1): m = RECORD.match(line) kind = "record" if not m: m = HEADING.match(line) kind = "heading" if not m: continue qid = int(m.group(2) if kind == "heading" else m.group(1)) tail = m.group(3) if kind == "heading" else m.group(2) state = "SECTION" if kind == "heading" else classify(tail) decls.append({ "qid": qid, "line": n, "kind": kind, "state": state, "word": (tail.lstrip("*").strip() or "")[:100], }) state = {} for d in decls: if d["kind"] == "record": state[d["qid"]] = d # last record wins, line order elif d["qid"] not in state: state[d["qid"]] = {"qid": d["qid"], "line": d["line"], "kind": "heading", "state": "SECTION", "word": d["word"]} return decls, state def open_ids(state): return sorted(q for q, d in state.items() if d["state"] == "OPEN") def opened_ids(decls): """Every id that carries at least one OPEN record, closed or not.""" return sorted({d["qid"] for d in decls if d["kind"] == "record" and d["state"] == "OPEN"}) def counts(decls, state): heads = [d for d in decls if d["kind"] == "heading"] recs = [d for d in decls if d["kind"] == "record"] return { "headings": len(heads), "heading_ids": len({d["qid"] for d in heads}), "records": len(recs), "record_ids": len({d["qid"] for d in recs}), "ids": len(state), "open": len(open_ids(state)), "opened": len(opened_ids(decls)), "record_only": len({d["qid"] for d in recs} - {d["qid"] for d in heads}), } # ---------------------------------------------------------------- the probes # # Each probe takes the repo root and returns (state, evidence). Evidence is # the thing a reader can check: a file:line, an rc, a count, a sentence. def read(repo, rel): return (repo / rel).read_text(encoding="utf-8", errors="replace") def run(argv, cwd, timeout=120): try: p = subprocess.run(argv, cwd=str(cwd), capture_output=True, text=True, timeout=timeout) return p.returncode, (p.stdout or "") + (p.stderr or "") except (OSError, subprocess.TimeoutExpired) as exc: return None, str(exc) def _load(path, name): spec = importlib.util.spec_from_file_location(name, str(path)) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) return mod def probe_q100(repo): """Q100: import_resolve_check's TEST_NAME must select `_test_x.py`.""" src = read(repo, "tools/import_resolve_check.py") pat = None for node in ast.walk(ast.parse(src)): if isinstance(node, ast.Assign): for t in node.targets: if isinstance(t, ast.Name) and t.id == "TEST_NAME": call = node.value if isinstance(call, ast.Call) and call.args: try: pat = ast.literal_eval(call.args[0]) except ValueError: pat = None if pat is None: return "STILL-OPEN", "TEST_NAME not found as a literal in import_resolve_check.py" rx = re.compile(pat) samples = ["wizard-work/forge/_test_roundtrip.py", "wizard-work/forge/test_log.txt", "wizard-work/forge/_test_roundtrip_refactored.py"] hit = [s for s in samples[:1] + samples[2:] if rx.search(s)] return ("REPAIRED" if len(hit) == 2 else "STILL-OPEN", "TEST_NAME=%r selects %d/2 underscore test files" % (pat, len(hit))) def probe_q101(repo): """Q101: ledger/census.py must at least parse.""" path = repo / "wizard-work/ledger/census.py" try: compile(path.read_text(encoding="utf-8"), str(path), "exec") except (OSError, SyntaxError) as exc: return "STILL-OPEN", "census.py will not compile: %s" % (exc,) return "REPAIRED", "census.py compiles (compile() at %s)" % path.name def probe_q102(repo): """Q102: three outcomes must be three channels.""" mod = _load(repo / "wizard-work/beam/majority_element.py", "q137_maj") try: got = mod.majority([None, None, 1]) none_majority = got is None except Exception: # noqa: BLE001 none_majority = False raised = False try: mod.majority([1, 2, 3]) except Exception as exc: # noqa: BLE001 raised = type(exc).__name__ == "NoMajority" return ("REPAIRED" if none_majority and raised else "STILL-OPEN", "majority([None,None,1])->None=%s; majority([1,2,3]) raises " "NoMajority=%s" % (none_majority, raised)) def probe_q103(repo): """Q103: queue_index must see ids of any width.""" rc, out = run([sys.executable, "tools/queue_index.py", "--check", "docs/queue-index.md"], repo) ok = rc == 0 return ("REPAIRED" if ok else "STILL-OPEN", "queue_index --check docs/queue-index.md rc=%s; %s" % (rc, out.strip().splitlines()[-1][:120] if out.strip() else "")) def probe_q104(repo): """Q104: undecodable JSONL must be a typed exit 2, no traceback.""" tmp = Path(tempfile.mkdtemp(prefix="q137-q104-")) try: bad = tmp / "bad.jsonl" bad.write_bytes(b'{"a": 1}\n\xff\xfe not utf-8\n') rc, out = run([sys.executable, "wizard-work/beam/jsonl_validate.py", str(bad)], repo) finally: shutil.rmtree(tmp, ignore_errors=True) typed = rc == 2 and "Traceback" not in out return ("REPAIRED" if typed else "STILL-OPEN", "undecodable input rc=%s traceback=%s" % (rc, "Traceback" in out)) def probe_q105(repo): """Q105/Q128: the beam gate must collect both test styles.""" txt = read(repo, "wizard-work/beam/CHECK") ok = "run_lane_tests.py" in txt return ("REPAIRED" if ok else "STILL-OPEN", "beam/CHECK runs run_lane_tests.py=%s (%d lines)" % (ok, len(txt.splitlines()))) def probe_q106(repo): """Q106: the dated sol gate must not pin an absolute host root.""" txt = read(repo, "wizard-work/sol/CHECK-2026-09-28") n = txt.count("/root") return ("REPAIRED" if n == 0 else "STILL-OPEN", "CHECK-2026-09-28 /root occurrences=%d" % n) def probe_q107(repo): """Q107: --dir must not exit the number of invalid files.""" tmp = Path(tempfile.mkdtemp(prefix="q137-q107-")) try: (tmp / "ok.json").write_text('{"a": 1}', encoding="utf-8") (tmp / "bad1.json").write_text("{nope", encoding="utf-8") (tmp / "bad2.json").write_text("{nope", encoding="utf-8") (tmp / "sub").mkdir() (tmp / "sub" / "deep.json").write_text('{"b": 2}', encoding="utf-8") rc, out = run([sys.executable, "wizard-work/spark/json-validator.py", str(tmp)], repo) finally: shutil.rmtree(tmp, ignore_errors=True) valid = sum(1 for ln in out.splitlines() if ln.startswith("VALID:")) invalid = sum(1 for ln in out.splitlines() if ln.startswith("INVALID:")) ok = rc == 1 and (valid, invalid) == (2, 2) return ("REPAIRED" if ok else "STILL-OPEN", "dir with 2 invalid + 2 valid (one nested): rc=%s valid=%d invalid=%d" % (rc, valid, invalid)) def probe_q109(repo): """Q109: snippet.py's tool check must measure, not assume.""" txt = read(repo, "wizard-work/spark/snippet.py") has_fn = "def check_tools" in txt returns_measurement = "available, error = check_tools" in txt graded = (repo / "wizard-work/spark/test_env_verifiers.py").is_file() ok = has_fn and returns_measurement and graded return ("REPAIRED" if ok else "STILL-OPEN", "check_tools=%s, call site=%s, graded by test_env_verifiers=%s" % (has_fn, returns_measurement, graded)) def probe_q110(repo): """Q110: the QUICKSTART's clone step must point at a live repository.""" txt = read(repo, "wizard-work/quill/QUICKSTART.md") clones = re.findall(r"^git clone (\S+)", txt, re.M) live = [u for u in clones if "forge.alexanderwhitestone.com" in u] ok = len(clones) == 1 and len(live) == 1 return ("REPAIRED" if ok else "STILL-OPEN", "%d git clone step(s): %s" % (len(clones), clones[:3])) def probe_q111(repo): """Q111: zero-length intervals must not skew the optimum.""" mod = _load(repo / "wizard-work/beam/weighted_interval_scheduling.py", "q137_wis") fn = getattr(mod, "weighted_interval_scheduling") def ok_pair(sel): for i in range(len(sel)): for j in range(i + 1, len(sel)): a, b = sel[i], sel[j] if min(a[1], b[1]) - max(a[0], b[0]) > 0: return False return True bad = 0 trials = 0 import random rng = random.Random(1371) for _ in range(60): n_try = rng.randint(1, 4) iv = [[rng.randint(0, 3), rng.randint(0, 3), rng.randint(1, 4)] for _ in range(n_try)] iv = [[min(a, b), max(a, b), w] for a, b, w in iv] got = fn(iv) best = 0 m = len(iv) for mask in range(1 << m): sel = [iv[i] for i in range(m) if mask >> i & 1] if ok_pair(sel) and sum(x[2] for x in sel) > best: best = sum(x[2] for x in sel) trials += 1 if got != best: bad += 1 return ("REPAIRED" if bad == 0 and trials else "STILL-OPEN", "zero-length included: %d mismatches in %d exhaustive trials" % (bad, trials)) def probe_q112(repo): """Q112: the census must see every heading, and every record.""" rc, out = run([sys.executable, "wizard-work/ledger/workqueue-census-v2/CHECK"], repo) line = "" for ln in out.splitlines(): if "distinct labels" in ln: line = ln.strip() if rc != 0 or not line: return "STILL-OPEN", "census CHECK rc=%s, no summary line" % rc m = re.search(r"\((\d+) rows, (\d+) distinct labels", line) text = read(repo, QUEUE) decls, state = parse(text) c = counts(decls, state) rows, labels = (int(m.group(1)), int(m.group(2))) if m else (0, 0) covered = rows >= c["headings"] and labels >= c["heading_ids"] return ("REPAIRED" if covered else "STILL-OPEN", "census %d rows/%d labels vs %d headings/%d heading ids; " "%d record-only ids are enumerated by the extractor, not as rows" % (rows, labels, c["headings"], c["heading_ids"], c["record_only"])) def probe_q113(repo): """Q113: the roster gate must fail on a seeded mutation.""" led = "wizard-work/ledger" tmp = Path(tempfile.mkdtemp(prefix="q137-q113-")) try: dst = tmp / led dst.mkdir(parents=True) for name in ("wizards.csv", "wizards.manifest.json"): shutil.copy2(repo / led / name, dst / name) man = json.loads((dst / "wizards.manifest.json").read_text(encoding="utf-8")) src_rel = man.get("source", "") if src_rel: (tmp / src_rel).parent.mkdir(parents=True, exist_ok=True) shutil.copy2(repo / src_rel, tmp / src_rel) csv = (dst / "wizards.csv").read_text(encoding="utf-8") (dst / "wizards.csv").write_text(csv.replace("Timmy", "GARBAGE NAME"), encoding="utf-8") bad_rc, _ = run([sys.executable, led + "/verify_wizards.py", "--root", str(tmp)], repo) good_rc, _ = run([sys.executable, led + "/verify_wizards.py", "--root", str(repo)], repo) finally: shutil.rmtree(tmp, ignore_errors=True) ok = bad_rc == 1 and good_rc == 0 return ("REPAIRED" if ok else "STILL-OPEN", "seeded mutation rc=%s, clean tree rc=%s" % (bad_rc, good_rc)) def probe_q115(repo): """Q115: an unreadable folder must not read as a mass deletion.""" txt = Path("/root/.hermes/scripts/troupe_pipe_monitor.py").read_text( encoding="utf-8", errors="replace") ok = "UNREADABLE" in txt and "carried" in txt return ("REPAIRED" if ok else "UNMEASURED", "monitor separates UNREADABLE=%s and carries the listing=%s" % ("UNREADABLE" in txt, "carried" in txt)) def probe_q117(repo): """Q117: an invented probe value must be data, not a recovery.""" txt = read(repo, "wizard-work/zeph/q60_enforce.py") ok = "KNOWN_PROBE" in txt and txt.count("KNOWN_PROBE") >= 3 return ("REPAIRED" if ok else "STILL-OPEN", "q60_enforce.py KNOWN_PROBE occurrences=%d" % txt.count("KNOWN_PROBE")) def probe_q118(repo): """Q118: a failed listing must be retried and report attempts/last.""" txt = Path("/root/.hermes/scripts/troupe_pipe_monitor.py").read_text( encoding="utf-8", errors="replace") retry = "for attempt in range(1, tries + 1)" in txt fields = "attempts=" in txt and "last=" in txt return ("REPAIRED" if retry and fields else "STILL-OPEN", "retry loop=%s, attempts=/last= fields=%s" % (retry, fields)) def probe_q119(repo): """Q119: an unmatched demand must not be silently 'info'.""" txt = read(repo, "wizard-work/sol/classify.py") ok = "unclassified" in txt and "Fallback" in txt return ("REPAIRED" if ok else "STILL-OPEN", "classify.py names an unclassified fallback in its header=%s" % ok) def probe_q120(repo): """Q120: the env check must import real dependencies and report versions.""" txt = read(repo, "wizard-work/spark/python-env-check.py") decl = "DEPENDENCIES" in txt called = len(re.findall(r"check_package\(", txt)) >= 2 return ("REPAIRED" if decl and called else "STILL-OPEN", "DEPENDENCIES table=%s, check_package call sites=%d" % (decl, len(re.findall(r"check_package\(", txt)))) def probe_q121(repo): """Q121: fixture absence must be asserted per value, not over a join.""" txt = read(repo, "wizard-work/zeph/q60_enforce.py") per_value = "for value in KNOWN_FIXTURE.values()" in txt or \ "KNOWN_FIXTURE.values()" in txt mutant = "two_entry" in txt ok = per_value and mutant return ("REPAIRED" if ok else "STILL-OPEN", "per-value absence=%s, two-entry mutant cell=%s" % (per_value, mutant)) def probe_q122(repo): """Q122: the census CHECK must not gate on a wall clock.""" rc, out = run([sys.executable, "wizard-work/ledger/workqueue-census-v2/CHECK"], repo) skipped = "SKIP" in out.upper() return ("REPAIRED" if rc == 0 else "STILL-OPEN", "census CHECK rc=%s, clock leg reads %s" % (rc, "SKIP" if skipped else "graded")) def unmade(reason): def probe(repo): return "UNMEASURED", reason return probe PROBES = { 100: probe_q100, 101: probe_q101, 102: probe_q102, 103: probe_q103, 104: probe_q104, 105: probe_q105, 106: probe_q106, 107: probe_q107, 109: probe_q109, 110: probe_q110, 111: probe_q111, 112: probe_q112, 113: probe_q113, 115: probe_q115, 117: probe_q117, 118: probe_q118, 119: probe_q119, 120: probe_q120, 121: probe_q121, 122: probe_q122, 51: unmade("gate-wide probe window: needs the live gate, not a file read"), 52: unmade("archive attribution: needs pipeline logs this tree does not hold"), 53: unmade("digest domain + state.db: needs the live database"), 65: unmade("population claim over 87 verifiers; the Q87 gate owns it now"), 114: unmade("free-tier rows contradict six provider endpoints: network"), 116: unmade("OpenTrain validThrough is per-render: network, time-lapsed"), } PIN_OPEN = [] PIN_OPENED = [51, 52, 53, 65, 100, 101, 102, 103, 104, 105, 106, 107, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122] # Q147 (2026-11-01): the record count, the id count and the record-only count # move with any append to WORK-QUEUE.md -- this one added Q147, which is a # record and an id and a record-only id, and no heading. Re-taken at the commit # that carries the record: 106 -> 107 records, 137 -> 138 ids, 72 -> 73 # record_ids, 51 -> 52 record_only; headings, heading_ids, open and opened # unchanged (97 / 86 / 0 / 26), so no id changed its state. # Q148 (2026-11-01): the same move for the same reason -- one appended record, # one appended id, no heading: 107 -> 108 records, 138 -> 139 ids, 73 -> 74 # record_ids, 52 -> 53 record_only; headings, heading_ids, open and opened # unchanged (97 / 86 / 0 / 26), so again no id changed its state. # Q150 (2026-11-01): the same move again -- one appended record, one appended # id, no heading: 109 -> 110 records, 140 -> 141 ids, 75 -> 76 record_ids, # 54 -> 55 record_only; headings, heading_ids, open and opened unchanged # (97 / 86 / 0 / 26). Q150 is a same-tick repair, so it never sat open and is # not registered as a probe -- CHECK-q150 is its regression guard, not a # closure probe (the Q138 rule for same-tick ids). PIN_COUNTS = {"headings": 97, "heading_ids": 86, "records": 110, "record_ids": 76, "ids": 141, "open": 0, "opened": 26, "record_only": 55} PIN_STATE = { 100: "REPAIRED", 101: "REPAIRED", 102: "REPAIRED", 103: "REPAIRED", 104: "REPAIRED", 105: "REPAIRED", 106: "REPAIRED", 107: "REPAIRED", 109: "REPAIRED", 110: "REPAIRED", 111: "REPAIRED", 112: "REPAIRED", 113: "REPAIRED", 115: "REPAIRED", 117: "REPAIRED", 118: "REPAIRED", 119: "REPAIRED", 120: "REPAIRED", 121: "REPAIRED", 122: "REPAIRED", 51: "UNMEASURED", 52: "UNMEASURED", 53: "UNMEASURED", 65: "UNMEASURED", 114: "UNMEASURED", 116: "UNMEASURED", } # ---------------------------------------------------------------- the selftest FIXTURES = [ ("open-then-done", "**Q5 \u2014 OPEN (a).** body\n**Q5 \u2014 DONE at `abc`.**\n", 5, "CLOSED"), ("opened-and-repaired-in-place", "**Q7 \u2014 OPENED and REPAIRED in the same tick (volt).** body\n", 7, "CLOSED"), ("open-with-no-close", "**Q9 \u2014 OPEN (pipe).** body\n", 9, "OPEN"), ("heading-only", "## Q11 \u2014 A section title\nbody\n", 11, "SECTION"), ("heading-then-record", "## Q13 \u2014 A section title\nbody\n**Q13 \u2014 DONE.**\n", 13, "CLOSED"), ("bold-not-a-declaration", "**Q15 measured.** prose, no dash\n", 15, None), ("width-boundary", "**Q1000 \u2014 OPEN (x).**\n**Q100 \u2014 DONE.**\n", 100, "CLOSED"), ("reopened-after-done", "**Q17 \u2014 DONE.**\n**Q17 \u2014 REOPENED (pipe).**\n", 17, "OPEN"), ] def graded(repo, text): """mode_check's rc with its output swallowed -- for the mutant cells.""" buf = io.StringIO() try: with redirect_stdout(buf): return mode_check(repo, text) finally: buf.close() def selftest(): cells = [] for name, text, qid, want in FIXTURES: _, state = parse(text) got = state.get(qid, {}).get("state") cells.append((name, got == want, "got=%r want=%r" % (got, want))) repo, text = load_repo(str(Path(__file__).resolve().parents[3])) if repo is not None and text is not None: once = parse(text) cells.append(("parse-is-deterministic", once == parse(text), "")) cells.append(("gate-green-control", graded(repo, text) == 0, "rc")) cells.append(("gate-new-open-id", graded(repo, text + "\n**Q999 \u2014 OPEN (x).**\n") == 1, "rc")) cells.append(("gate-done-turned-open", graded(repo, text.replace("**Q117 \u2014 DONE", "**Q117 \u2014 OPEN", 1)) == 1, "rc")) cells.append(("gate-heading-dropped", graded(repo, re.sub(r"(?m)^## Q5 \u2014.*\n", "", text, count=1)) == 1, "rc")) bad = [c for c in cells if not c[1]] for name, ok, detail in cells: print("%-28s %s %s" % (name, "PASS" if ok else "FAIL", detail)) print("SELFTEST %s (%d cells, %d failed)" % ("OK" if not bad else "FAILED", len(cells), len(bad))) return 0 if not bad else 1 def parse_text_roundtrip(): return "**Q1 \u2014 OPEN (x).**\n" # ---------------------------------------------------------------- the modes def load_repo(arg): root = Path(arg).resolve() q = root / QUEUE if not q.is_file(): print("cannot grade: no %s under %s" % (QUEUE, root)) return None, None return root, q.read_text(encoding="utf-8") def mode_check(repo, text): decls, state = parse(text) c = counts(decls, state) got = open_ids(state) print("queue: %d lines, %d headings/%d ids, %d records/%d ids, " "%d record-only ids" % (len(text.splitlines()), c["headings"], c["heading_ids"], c["records"], c["record_ids"], c["record_only"])) print("declared open: %d; ids ever opened: %d" % (len(got), c["opened"])) faults = [] for key in sorted(PIN_COUNTS): if c[key] != PIN_COUNTS[key]: faults.append("count %s moved %d -> %d" % (key, PIN_COUNTS[key], c[key])) if got != PIN_OPEN: added = [q for q in got if q not in PIN_OPEN] gone = [q for q in PIN_OPEN if q not in got] faults.append("open set moved: +%s -%s" % (added, gone)) if opened_ids(decls) != PIN_OPENED: faults.append("the set of ever-opened ids moved") for f in faults: print("FAIL %s" % f) print("CHECK %s" % ("OK: 0 open, %d ever opened, counts match the pin" % len(PIN_OPENED) if not faults else "%d fault(s)" % len(faults))) return 0 if not faults else 1 def mode_order(repo, text): """Line order must be commit order, or 'last record' is not 'latest'.""" decls, _ = parse(text) recs = [d for d in decls if d["kind"] == "record"] rc, out = run(["git", "blame", "--porcelain", "--", QUEUE], repo) if rc != 0 or not out: print("cannot grade order: git blame failed") return 2 by_line = {} commit = None for ln in out.splitlines(): m = re.match(r"^([0-9a-f]{40}) \d+ (\d+)(?: \d+)?$", ln) if m: commit = m.group(1) by_line[int(m.group(2))] = commit times = {} stamps = [] pending = 0 for d in recs: c = by_line.get(d["line"]) if c is None: print("cannot grade order: no blame entry for line %d" % d["line"]) return 2 if set(c) == {"0"}: pending += 1 # appended, not yet committed continue if c not in times: trc, tout = run(["git", "show", "-s", "--format=%ct", c], repo) if trc != 0: print("cannot grade order: no timestamp for %s" % c[:12]) return 2 times[c] = int(tout.strip()) stamps.append((d["line"], times[c], c)) if not stamps: print("cannot grade order: no committed record lines") return 2 bad = [] for (l1, t1, c1), (l2, t2, c2) in zip(stamps, stamps[1:]): if t2 < t1: bad.append("line %d (%s) is newer than line %d (%s)" % (l2, c2[:8], l1, c1[:8])) print("record order: %d commits over %d committed records (%d pending " "commit); %d inversion(s)" % (len(times), len(stamps), pending, len(bad))) for b in bad[:5]: print("FAIL %s" % b) print("CHECK %s" % ("OK: line order is commit order, so 'last record' is " "'latest'" if not bad else "%d inversion(s)" % len(bad))) return 0 if not bad else 1 def mode_table(repo, text): decls, state = parse(text) for q in opened_ids(decls): d = state[q] print("Q%-4d %-6s :%-6d %s" % (q, d["state"], d["line"], d["word"][:90])) return 0 def mode_closures(repo, text): decls, state = parse(text) want = opened_ids(decls) if want != PIN_OPENED: print("cannot grade closures: the ever-opened set moved, run --check") return 2 faults = [] rows = [] for q in want: probe = PROBES.get(q) if probe is None: rows.append((q, "UNMEASURED", "no probe registered")) continue try: got, ev = probe(repo) except Exception as exc: # noqa: BLE001 print("Q%-4d UNGRADABLE %s: %s" % (q, type(exc).__name__, exc)) return 2 rows.append((q, got, ev)) tally = {} for q, got, ev in rows: tally[got] = tally.get(got, 0) + 1 pin = PIN_STATE.get(q) mark = " " if got == pin else "!" print("%sQ%-4d %-10s %s" % (mark, q, got, ev)) if got != pin: faults.append("Q%d moved %s -> %s" % (q, pin, got)) print("closure tally: %s" % ", ".join("%s %d" % kv for kv in sorted(tally.items()))) for f in faults: print("FAIL %s -- re-pin with the commit that moved it" % f) print("CHECK %s" % ("OK: %d ever-opened ids, %d measured, states match the pin" % (len(want), len(rows) - tally.get("UNMEASURED", 0)) if not faults else "%d fault(s)" % len(faults))) return 0 if not faults else 1 def main(argv=None): ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) ap.add_argument("--repo", default=str(Path(__file__).resolve().parents[3])) ap.add_argument("--check", action="store_true", help="grade the declared set") ap.add_argument("--closures", action="store_true", help="grade measured states") ap.add_argument("--order", action="store_true", help="line order vs commit order") ap.add_argument("--table", action="store_true", help="print the open set") ap.add_argument("--selftest", action="store_true", help="parser cells") args = ap.parse_args(argv) if args.selftest: return selftest() repo, text = load_repo(args.repo) if repo is None: return 2 if args.table: return mode_table(repo, text) if args.order: return mode_order(repo, text) if args.closures: return mode_closures(repo, text) return mode_check(repo, text) if __name__ == "__main__": sys.exit(main())