Files
troupe-standup/wizard-work/ledger/2026-10-29-checkin-census/grade.py
T
volt 76471add2d ledger: both later check-in censuses graded at their own revision (Q151)
The 2026-10-28 and 2026-10-29 check-in censuses were red at d42f577 for
three reasons at once: both CHECKs were Python under a 'bash CHECK' line
(rc 2, import: command not found), both graded the live tree (so every new
check-in reddened a dated artifact), and both hardcoded /root/troupe-standup.
The 10-28 table also carried a 101st row for checkins/2026-10-27-sentinel.md,
a file in no revision.

- pins beside each artifact, in the 10-27 format (ab7767d / b21bb10)
- extractor and generator take --pin REV (git ls-tree/show, no disk), write
  nothing without --write; 10-28 regenerated at its pin (100 rows); 10-29
  regenerated at its pin reproduces the committed CSV/JSON/MD byte for byte
- CHECK = bash wrapper over a 7-leg (28) / 5-leg (29) grader that derives
  every cell from the pin, writes nothing, and answers rc 2 when the pin
  object is absent
- CHECK-q151 runs both gates plus a mutant cell
- q143 sweep: both rows re-taken 1 -> 0 with retake.py, CHECK-q151 added;
  q137 open-set pin moved with the record
2026-11-01 21:44:26 +00:00

238 lines
9.5 KiB
Python
Executable File

#!/usr/bin/env python3
"""Grade the 2026-10-29 check-in signee census against its PINNED revision.
WHAT IT GRADES -- five legs, every one re-derived from `git ls-tree` / `git show`
/ `git log` at the pinned commit, compared against the committed tables. Nothing
is read from the working tree (so tomorrow's check-in cannot red this artifact)
and nothing is written (the previous revision's legs ran against `git ls-files`,
which is the live index, and its row set grew with the archive).
1 scope the pin file beside this script: header, sortedness, uniqueness, names
2 pin the dated name list equals `checkins/` at the pinned commit (SKIP if
the object is not in this database -- the committed list is then the pin)
3 csv every cell of the committed CSV against the re-derivation, plus the
`checkins/README.md` index row and the header order
4 md the MD's table block, row for row, against the same derivation
5 json the JSON's rows and `total_checkin_rows`
EXIT CODES: 0 every leg passed; 1 a fault (including "the pin is not a usable
input"); 2 cannot grade at all (no git).
Usage: python3 grade.py [--repo DIR] (or `bash CHECK`, which runs this file)
"""
import argparse
import csv
import json
import re
import subprocess
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
PIN_FILE = HERE / "2026-10-29-checkin-census.files"
TAG = "2026-10-29"
CSV_PATH = HERE / "checkin-census.csv"
MD_PATH = HERE / "checkin-census.md"
JSON_PATH = HERE / "checkin-census.json"
INDEX_FILE = "checkins/README.md"
FIELDS = ["file", "date", "signee", "headline", "committer", "kind"]
NAME_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})-(.+)\.md$")
PIN_RE = re.compile(r"^# pin checkins@([0-9a-f]{40}) tree=([0-9a-f]{40})$")
class Fault(Exception):
"""A graded leg failed; the message is the verdict line."""
class CannotGrade(Exception):
"""The instrument itself cannot run."""
def git(repo, *args):
try:
out = subprocess.run(["git", "-C", str(repo)] + list(args),
capture_output=True, timeout=180)
except (OSError, subprocess.SubprocessError) as exc:
raise CannotGrade("git %s: %s" % (args[0], exc))
if out.returncode != 0:
raise Fault("git %s failed: %s" % (" ".join(args),
out.stderr.decode("utf-8", "replace").strip()))
return out.stdout
def find_repo():
for cand in (HERE, HERE.parent):
r = subprocess.run(["git", "-C", str(cand), "rev-parse", "--show-toplevel"],
capture_output=True, text=True)
if r.returncode == 0 and r.stdout.strip():
return Path(r.stdout.strip())
return HERE.parent.parent.parent
def require_pin(repo, sha):
"""A revision this clone does not carry cannot grade a census of it."""
try:
git(repo, "cat-file", "-e", sha + "^{commit}")
except Fault:
raise CannotGrade("pin %s is not in this object database: a census of that "
"revision cannot be graded without it" % sha[:12])
def read_pin():
if not PIN_FILE.is_file() or PIN_FILE.stat().st_size == 0:
raise Fault("SCOPE NOT ESTABLISHED: pinned name list missing or empty: %s"
% PIN_FILE.name)
lines = PIN_FILE.read_text(encoding="utf-8").splitlines()
m = PIN_RE.match(lines[0])
if not m:
raise Fault("SCOPE NOT ESTABLISHED: pin header not understood: %r" % lines[0])
names = lines[1:]
if not names:
raise Fault("SCOPE NOT ESTABLISHED: pin names no files")
bad = [n for n in names if not NAME_RE.match(n)]
if bad:
raise Fault("SCOPE NOT ESTABLISHED: %d pinned name(s) are not YYYY-MM-DD-*.md "
"(first: %r)" % (len(bad), bad[0]))
if len(set(names)) != len(names):
raise Fault("SCOPE NOT ESTABLISHED: duplicated name(s) in the pin")
if sorted(names) != names:
raise Fault("SCOPE NOT ESTABLISHED: pin names are not sorted (LC_ALL=C)")
return m.group(1), m.group(2), names
def leg_pin(repo, sha, tree, names):
real = git(repo, "rev-parse", ("%s:checkins" % sha)).decode().strip()
if real != tree:
raise Fault("pin tree %s is not %s:checkins (%s)" % (tree[:12], sha[:7], real[:12]))
out = git(repo, "ls-tree", "-r", "--name-only", sha, "--", "checkins/").decode("utf-8")
at_pin = sorted(p[len("checkins/"):] for p in out.splitlines()
if p.startswith("checkins/") and NAME_RE.match(p[len("checkins/"):]))
if at_pin != names:
only_pin = [n for n in at_pin if n not in set(names)]
only_list = [n for n in names if n not in set(at_pin)]
raise Fault("pin name list != checkins/ at %s (+%d at pin, +%d in pin): %s %s"
% (sha[:7], len(only_pin), len(only_list), only_pin[:3], only_list[:3]))
return "PASS pin: %d dated name(s) == checkins/ tree %s at %s" % (
len(names), tree[:12], sha[:7])
def derive(repo, sha, names):
"""Rows in the artifact's order: (date, file), plus the README index row."""
rows = []
for name in names:
path = "checkins/" + name
m = NAME_RE.match(name)
signee = re.sub(r"-\d+$", "", m.group(2))
text = git(repo, "show", "%s:%s" % (sha, path)).decode("utf-8", "replace")
head = ""
for line in text.split("\n"):
if line.startswith("# "):
head = line[2:].strip()
break
who = git(repo, "log", "-1", "--format=%an <%ae>", sha, "--", path).decode().strip()
rows.append({"file": path, "date": m.group(1), "signee": signee,
"headline": head, "committer": who or "(no committer)",
"kind": "checkin"})
tracked = git(repo, "ls-tree", "-r", "--name-only", sha, "--", "checkins/").decode()
if INDEX_FILE in tracked.splitlines():
rows.append({"file": INDEX_FILE, "date": TAG, "signee": "troupe-pipe (index)",
"headline": "Check-ins index",
"committer": "(index file; no committer)", "kind": "index"})
rows.sort(key=lambda r: (r["date"], r["file"]))
return rows
def leg_csv(rows):
with open(CSV_PATH, encoding="utf-8", newline="") as fh:
reader = csv.reader(fh)
header = next(reader)
got = list(reader)
if header != FIELDS:
raise Fault("CSV header %r != %r" % (header, FIELDS))
want = [[r[f] for f in FIELDS] for r in rows]
if len(got) != len(want):
raise Fault("CSV has %d row(s); the pin derives %d" % (len(got), len(want)))
for i, (g, w) in enumerate(zip(got, want)):
if g != w:
raise Fault("CSV row %d: %r != pin %r" % (i, g, w))
return "PASS csv: %d row(s) match the pin cell for cell" % len(got)
def md_rows(text):
out, inside = [], False
for line in text.splitlines():
if not line.startswith("|"):
if inside:
break
continue
cells = [c.strip() for c in line.strip().strip("|").split("|")]
if not inside:
inside = cells == FIELDS
continue
if set("".join(cells)) <= set("-: "):
continue
out.append(cells)
return out
def leg_md(rows):
got = md_rows(MD_PATH.read_text(encoding="utf-8"))
want = [[r[f] for f in FIELDS] for r in rows]
if got != want:
bad = next((i for i in range(min(len(got), len(want))) if got[i] != want[i]),
min(len(got), len(want)))
raise Fault("MD table: %d row(s) vs pin %d; first difference at row %d "
"(md %r, pin %r)" % (len(got), len(want), bad,
got[bad] if bad < len(got) else None,
want[bad] if bad < len(want) else None))
return "PASS md: %d row(s) match the pin" % len(got)
def leg_json(rows):
doc = json.loads(JSON_PATH.read_text(encoding="utf-8"))
got = [{f: r.get(f) for f in FIELDS} for r in doc.get("rows", [])]
want = [{f: r[f] for f in FIELDS} for r in rows]
if got != want:
bad = next((i for i in range(min(len(got), len(want))) if got[i] != want[i]),
min(len(got), len(want)))
raise Fault("JSON rows: %d vs pin %d; first difference at row %d" % (
len(got), len(want), bad))
n_dated = len([r for r in rows if r["kind"] == "checkin"])
if doc.get("total_checkin_rows") != n_dated:
raise Fault("JSON total_checkin_rows %r != %d derived" % (
doc.get("total_checkin_rows"), n_dated))
return "PASS json: %d row(s), total_checkin_rows=%d, match the pin" % (len(got), n_dated)
def main(argv=None):
ap = argparse.ArgumentParser(description="grade the 2026-10-29 check-in census")
ap.add_argument("--repo", default=None, help="repository to grade (default: this tree's)")
args = ap.parse_args(argv)
repo = Path(args.repo) if args.repo else find_repo()
try:
sha, tree, names = read_pin()
require_pin(repo, sha)
print("PASS scope: %d pinned dated file(s), pin %s" % (len(names), sha[:7]))
lines = [leg_pin(repo, sha, tree, names)]
rows = derive(repo, sha, names)
lines.append(leg_csv(rows))
lines.append(leg_md(rows))
lines.append(leg_json(rows))
except Fault as exc:
print("FAIL: %s" % exc)
return 1
except CannotGrade as exc:
print("CANNOT GRADE: %s" % exc)
return 2
for line in lines:
print(line)
print("VERDICT=PASS census-%s rows=%d dated=%d pin=%s"
% (TAG, len(rows), len(rows) - 1, sha[:7]))
return 0
if __name__ == "__main__":
sys.exit(main())