volt: Q168 land -- the append audit grades the pin axis beside the index axis

queue_append_audit.py now runs two graders out of one `git archive`: grade_index
(unchanged vocabulary) and grade_pin, which runs the rev's own open_set.py
--check beside the rev's own queue. PASS / DRIFT / UNGRADABLE / ERROR; rc 1 when
either axis is fatal, so --guard means "index current and pin moved".
--axis {both,index,pin} narrows the run; the report prints index:/pin:/drift:.

Checks landed with it: CHECK-append-pin (5 legs, rc 0 here).
Measured at 12fb1b0's parent line: --selftest 43/43; --guard rc 0 pin:PASS;
1c3c855..56d3853 rc 1 pin:DRIFT "count headings moved 116 -> 117" while
--axis index over the same range reads rc 0, and 12fb1b0's pre-axis blob reads
rc 0 too, so the red is the axis and not the range.

PIN_COUNTS moved to the post-append counts (119 headings/103 ids, 131
records/97 ids) and docs/queue-index.md re-rendered; docs/TOOLS.md row follows
the tool's flags.
This commit is contained in:
volt committed 2026-11-04 12:47:43 +00:00
1 parent 16332d6709
commit 1ec068cae0
7 files changed
+487 -95

No files matched your search

+276 -78
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
"""queue_append_audit.py -- was the queue's index current IN THE TREE of every
commit that touched the queue?
"""queue_append_audit.py -- did every queue-touching commit leave its own tree
consistent: its index current, and its pin moved to where the queue's counts
went?
WHY THIS EXISTS
`docs/queue-index.md` is rendered from `WORK-QUEUE.md` and pins it by sha256,
@@ -22,7 +23,9 @@ WHY THIS EXISTS
the gate as it existed there.
WHAT IT MEASURES -- one row per commit that touches the queue in the range
verdict PASS the commit's own tree passes its own --check (rc 0)
verdict the INDEX axis: the commit's own tree passes its own
`tools/queue_index.py --check` (rc 0)
PASS rc 0
STALE --check graded a stale rendered document (rc 1)
UNGRADABLE the gate did not exist in that tree (no
tools/queue_index.py, or no index document), or the
@@ -32,6 +35,19 @@ WHAT IT MEASURES -- one row per commit that touches the queue in the range
bucket is the exact defect this tool was built after.
ERROR any other exit, a crash, or a timeout. rc 1 without
the tool's `STALE:` line is a crash, not a fault.
pin the PIN axis: the same tree's own
`wizard-work/volt/q137-open-set/open_set.py --check`, which
compares WORK-QUEUE.md's counts with the PIN_COUNTS that file
carries.
PASS rc 0, the pin moved with the append
DRIFT rc 1, the queue moved and the pin did not
UNGRADABLE no open_set.py at that rev (the pin is younger than
the queue), or the tool refused with rc 2
ERROR any other exit, a crash, or a timeout
The two axes are graded in ONE materialised tree and reported
side by side, because neither stands for the other: `56d3853`
re-rendered its index and left the pin two headings behind, so
its row reads `PASS` on the index and `DRIFT` on the pin.
queue_add/del numstat for the queue in that commit.
index_changed whether the same commit also changed the index document -- the
mechanical shape of the rule, reported BESIDE the verdict and
@@ -41,7 +57,8 @@ WHAT IT MEASURES -- one row per commit that touches the queue in the range
would be such a case, and the verdict would say so).
Precedence is deliberate: the verdict comes from a checkout of the commit,
never from its message, and never from a run at HEAD.
never from its message, and never from a run at HEAD. `--axis` narrows the run
to one of the two axes; the default grades both.
WHAT IT DOES NOT DO
It repairs nothing, decides nothing about whether an UNGRADABLE commit should
@@ -49,7 +66,7 @@ WHAT IT DOES NOT DO
`tools/queue_claim_check.py`'s verdict. `--guard` is the same measurement
restricted to the NEWEST queue-touching commit, which is the shape a tick's
`check.sh` can call: rc 0 means the commit about to be pushed left the index
current in its own tree.
current AND the pin moved in its own tree.
EXIT CODES (house contract, decided 2026-10-17 at 6ee8fce)
0 ran; nothing graded faulty (UNGRADABLE rows are reported, not faults)
@@ -78,8 +95,15 @@ import tempfile
QUEUE_DEFAULT = "WORK-QUEUE.md"
INDEX_DEFAULT = "docs/queue-index.md"
GATE_TOOL = "tools/queue_index.py"
# The queue's own pin, graded in the SAME materialised tree: `open_set.py
# --check` compares WORK-QUEUE.md's counts against the PIN_COUNTS the file
# carries. The index axis cannot see it -- `56d3853` rendered its index and left
# the pin two headings behind -- so the audit grades both axes, each reported
# beside the other and neither standing for the other.
PIN_TOOL = "wizard-work/volt/q137-open-set/open_set.py"
HERE = os.path.dirname(os.path.abspath(__file__))
FATAL = ("STALE", "ERROR")
PIN_FATAL = ("DRIFT", "ERROR")
# ---------------------------------------------------------------- git plumbing
@@ -166,61 +190,126 @@ def materialise(repo, rev, paths, dest):
# ------------------------------------------------------------------- one commit
def audit_commit(repo, rev, queue, index, timeout):
"""Grade one commit IN ITS OWN TREE. Never raises."""
def run_check(cmd, cwd, timeout):
"""(rc, stdout, stderr, why) -- a timeout is a verdict, never a traceback."""
try:
p = subprocess.run(cmd, cwd=cwd, capture_output=True, timeout=timeout)
except subprocess.TimeoutExpired:
return None, "", "", "--check timed out after %ss" % timeout
return (p.returncode, p.stdout.decode("utf-8", "replace"),
p.stderr.decode("utf-8", "replace"), "")
def grade_index(td, queue, index, timeout):
"""The index axis, measured in `td`: {verdict, rc, detail}."""
tool = os.path.join(td, GATE_TOOL)
cmd = [sys.executable, tool,
"--repo", td,
"--queue", os.path.join(td, queue),
"--check", os.path.join(td, index)]
rc, s, e, why = run_check(cmd, td, timeout)
if why:
return {"verdict": "ERROR", "rc": None, "detail": why}
first = (s.strip() or e.strip() or "").splitlines()
out = {"rc": rc, "detail": first[0][:180] if first else ""}
if rc == 0:
out["verdict"] = "PASS"
elif rc == 1 and "STALE:" in s:
out["verdict"] = "STALE"
elif rc == 2:
out["verdict"] = "UNGRADABLE"
else:
# rc 1 with no STALE line is a crash wearing the verdict code; rc 3
# is retired. Neither is a graded fault, and neither is a pass.
out["verdict"] = "ERROR"
return out
def grade_pin(td, timeout):
"""The pin axis, measured in `td`: {pin, pin_rc, pin_detail}.
`open_set.py --check` compares the queue's own counts against the PIN_COUNTS
the file carries, so a commit that appends a heading or a record and leaves
the pin where it was reads DRIFT here even when its index is current -- the
exact shape `56d3853` landed in, which the index axis read PASS.
"""
tool = os.path.join(td, PIN_TOOL)
rc, s, e, why = run_check([sys.executable, tool, "--check"], td, timeout)
if why:
return {"pin": "ERROR", "pin_rc": None, "pin_detail": why}
lines = (s.strip() or e.strip() or "").splitlines()
fault = [ln for ln in lines if ln.startswith("FAIL")]
out = {"pin_rc": rc, "pin_detail": (fault[0] if fault else (lines[-1] if lines else ""))[:180]}
if rc == 0:
out["pin"] = "PASS"
elif rc == 1:
out["pin"] = "DRIFT"
elif rc == 2:
# the pin tool itself refused: a tree it cannot read is not a drift
out["pin"] = "UNGRADABLE"
else:
out["pin"] = "ERROR"
return out
def audit_commit(repo, rev, queue, index, timeout, axis="both"):
"""Grade one commit IN ITS OWN TREE. Never raises.
Two axes, graded independently in one materialised tree, because neither
stands for the other: a commit can leave the rendered index current and the
queue's own pin two headings behind (Q167's landing), and a commit can move
the pin while shipping no index at all.
"""
row = {"rev": rev}
row.update(commit_meta(repo, rev))
add, delete, touched_index = numstat(repo, rev, queue, index)
row.update({"queue_add": add, "queue_del": delete,
"index_changed": touched_index, "rc": None, "detail": ""})
"index_changed": touched_index, "rc": None, "detail": "",
"pin_rc": None, "pin_detail": ""})
want_index = axis in ("both", "index")
want_pin = axis in ("both", "pin")
if not exists(repo, rev, queue):
row.update(verdict="UNGRADABLE",
detail="%s does not exist at this rev" % queue)
missing = "%s does not exist at this rev" % queue
row.update(verdict="UNGRADABLE", detail=missing,
pin="UNGRADABLE", pin_detail=missing)
return row
# The gate as it existed AT THAT REV, not as it exists today: a revision
# where queue_index.py had not been written yet has no gate, and no claim
# about its index can be true or false.
if not exists(repo, rev, GATE_TOOL):
row.update(verdict="UNGRADABLE",
detail="no %s at this rev (the gate did not exist yet)" % GATE_TOOL)
return row
if not exists(repo, rev, index):
row.update(verdict="UNGRADABLE",
detail="no %s at this rev (nothing to compare against)" % index)
if want_index:
# The gate as it existed AT THAT REV, not as it exists today: a revision
# where queue_index.py had not been written yet has no gate, and no
# claim about its index can be true or false.
if not exists(repo, rev, GATE_TOOL):
row.update(verdict="UNGRADABLE",
detail="no %s at this rev (the gate did not exist yet)" % GATE_TOOL)
want_index = False
elif not exists(repo, rev, index):
row.update(verdict="UNGRADABLE",
detail="no %s at this rev (nothing to compare against)" % index)
want_index = False
if want_pin and not exists(repo, rev, PIN_TOOL):
# the pin is younger than the queue: absent is UNGRADABLE, never DRIFT
row.update(pin="UNGRADABLE",
pin_detail="no %s at this rev" % PIN_TOOL)
want_pin = False
if not want_index and not want_pin:
return row
wanted = [p for p in (queue, index, GATE_TOOL, PIN_TOOL) if exists(repo, rev, p)]
td = tempfile.mkdtemp(prefix="qaa-")
try:
ok, why = materialise(repo, rev, [queue, index, GATE_TOOL], td)
ok, why = materialise(repo, rev, wanted, td)
if not ok:
row.update(verdict="ERROR", detail="could not materialise: %s" % why)
why = "could not materialise: %s" % why
if want_index:
row.update(verdict="ERROR", detail=why)
if want_pin:
row.update(pin="ERROR", pin_detail=why)
return row
tool = os.path.join(td, GATE_TOOL)
cmd = [sys.executable, tool,
"--repo", td,
"--queue", os.path.join(td, queue),
"--check", os.path.join(td, index)]
try:
p = subprocess.run(cmd, cwd=td, capture_output=True, timeout=timeout)
except subprocess.TimeoutExpired:
row.update(verdict="ERROR", detail="--check timed out after %ss" % timeout)
return row
s = p.stdout.decode("utf-8", "replace")
e = p.stderr.decode("utf-8", "replace")
first = (s.strip() or e.strip() or "").splitlines()
row["detail"] = first[0][:180] if first else ""
row["rc"] = p.returncode
if p.returncode == 0:
row["verdict"] = "PASS"
elif p.returncode == 1 and "STALE:" in s:
row["verdict"] = "STALE"
elif p.returncode == 2:
row["verdict"] = "UNGRADABLE"
else:
# rc 1 with no STALE line is a crash wearing the verdict code; rc 3
# is retired. Neither is a graded fault, and neither is a pass.
row["verdict"] = "ERROR"
if want_index:
row.update(grade_index(td, queue, index, timeout))
if want_pin:
row.update(grade_pin(td, timeout))
return row
finally:
shutil.rmtree(td, ignore_errors=True)
@@ -238,7 +327,7 @@ def queue_revs(repo, queue, rev_from, rev_to):
def audit(repo, queue, index, rev_from, rev_to, guard=False, timeout=30,
all_history=False):
all_history=False, axis="both"):
"""(rc, payload). The only place exit codes are decided.
Range rules, in order:
@@ -253,8 +342,9 @@ def audit(repo, queue, index, rev_from, rev_to, guard=False, timeout=30,
"""
payload = {"repo": repo, "queue": queue, "index": index,
"from": None, "to": None, "guard": bool(guard),
"auto_from": False, "pre_gate": 0,
"rows": [], "counts": {}, "graded": 0, "rc": None}
"auto_from": False, "pre_gate": 0, "axis": axis,
"rows": [], "counts": {}, "pin_counts": {},
"graded": 0, "pin_graded": 0, "rc": None}
def _grade_error(msg):
"""Attach a grade error to the payload and return the (2, payload) verdict.
@@ -310,14 +400,21 @@ def audit(repo, queue, index, rev_from, rev_to, guard=False, timeout=30,
except (ValueError, AttributeError):
payload["pre_gate"] = 0
rows = [audit_commit(repo, r, queue, index, timeout) for r in revs]
counts = {}
rows = [audit_commit(repo, r, queue, index, timeout, axis) for r in revs]
counts, pin_counts = {}, {}
for r in rows:
counts[r["verdict"]] = counts.get(r["verdict"], 0) + 1
if "verdict" in r:
counts[r["verdict"]] = counts.get(r["verdict"], 0) + 1
if "pin" in r:
pin_counts[r["pin"]] = pin_counts.get(r["pin"], 0) + 1
payload["rows"] = rows
payload["counts"] = counts
payload["graded"] = counts.get("PASS", 0) + counts.get("STALE", 0) + counts.get("ERROR", 0)
payload["rc"] = 1 if any(r["verdict"] in FATAL for r in rows) else 0
payload["pin_counts"] = pin_counts
payload["graded"] = sum(counts.get(k, 0) for k in ("PASS", "STALE", "ERROR"))
payload["pin_graded"] = sum(pin_counts.get(k, 0) for k in ("PASS", "DRIFT", "ERROR"))
index_fault = any(r.get("verdict") in FATAL for r in rows)
pin_fault = any(r.get("pin") in PIN_FATAL for r in rows)
payload["rc"] = 1 if (index_fault or pin_fault) else 0
return payload["rc"], payload
@@ -349,30 +446,47 @@ def report(payload, stream=sys.stdout):
for r in payload["rows"]:
add, dele = r["queue_add"], r["queue_del"]
q = "q %s/%s" % (add if add is not None else "-", dele if dele is not None else "-")
w(" %-10s %-8s %-25s %-9s index:%-3s %s\n"
% (r["verdict"], r["short"], r["date"][:25], q,
"yes" if r["index_changed"] else "no", r["detail"]))
note = r.get("detail") or ""
if r.get("pin") and r["pin"] != "PASS":
note = "%s | pin: %s" % (note, r.get("pin_detail") or r["pin"])
w(" %-10s %-8s %-25s %-9s index:%-3s pin:%-10s %s\n"
% (r.get("verdict") or "-", r["short"], r["date"][:25], q,
"yes" if r["index_changed"] else "no", r.get("pin") or "-", note))
c = payload["counts"]
pc = payload["pin_counts"]
w("\nsummary: %d commit(s) -- %s\n"
% (len(payload["rows"]),
", ".join("%d %s" % (c[k], k) for k in ("PASS", "STALE", "UNGRADABLE", "ERROR") if k in c)
or "nothing graded"))
w("pin: %s\n"
% (", ".join("%d %s" % (pc[k], k) for k in ("PASS", "DRIFT", "UNGRADABLE", "ERROR") if k in pc)
or "not graded"))
ungradable = c.get("UNGRADABLE", 0)
w("graded: %d of %d%s\n"
w("graded: %d of %d index, %d of %d pin%s\n"
% (payload["graded"], len(payload["rows"]),
(" (%d could not be graded -- reported, NOT counted as stale)" % ungradable)
payload["pin_graded"], len(payload["rows"]),
(" (%d index UNGRADABLE -- reported, NOT counted as stale)" % ungradable)
if ungradable else ""))
stale = [r["short"] for r in payload["rows"] if r["verdict"] == "STALE"]
stale = [r["short"] for r in payload["rows"] if r.get("verdict") == "STALE"]
if stale:
w("stale: %s\n" % " ".join(stale))
bad = [r["short"] for r in payload["rows"] if r["verdict"] == "ERROR"]
drift = [r["short"] for r in payload["rows"] if r.get("pin") == "DRIFT"]
if drift:
w("drift: %s\n" % " ".join(drift))
bad = [r["short"] for r in payload["rows"] if r.get("verdict") == "ERROR"]
pbad = [r["short"] for r in payload["rows"] if r.get("pin") == "ERROR"]
if bad:
w("error: %s\n" % " ".join(bad))
if pbad:
w("pin err: %s\n" % " ".join(pbad))
if payload["rc"] == 0:
w("verdict: every graded commit left the index current in its own tree\n")
w("verdict: every graded commit left its own tree consistent -- "
"the index current and the pin where the queue put it\n")
else:
w("verdict: %d commit(s) did NOT leave the index current in their own tree -- "
"rc 1\n" % (len(stale) + len(bad)))
w("verdict: %d commit(s) left their own tree inconsistent -- "
"%d index, %d pin -- rc 1\n"
% (len(stale) + len(bad) + len(drift) + len(pbad), len(stale) + len(bad),
len(drift) + len(pbad)))
# ------------------------------------------------------------------- selftest
@@ -381,13 +495,40 @@ def _sh(cwd, *args):
return subprocess.run(args, cwd=cwd, capture_output=True, text=True)
def _fixture(base):
"""A five-commit history with one commit of each kind. Returns (repo, revs).
PIN_STUB = '''#!/usr/bin/env python3
"""A stand-in for wizard-work/volt/q137-open-set/open_set.py's --check: count
the queue's headings and compare them with the PIN this commit carries."""
import argparse
import sys
A: queue only, no gate tool -> UNGRADABLE
B: appends, and adds tools/queue_index.py + its render -> PASS
C: appends to the queue, no re-render -> STALE
D: appends AND re-renders -> PASS
QUEUE = "WORK-QUEUE.md"
PIN = @PIN@
ap = argparse.ArgumentParser()
ap.add_argument("--check", action="store_true")
ap.parse_args()
n = len([ln for ln in open(QUEUE, encoding="utf-8") if ln.startswith("## ")])
print("queue: %d heading(s)" % n)
if n != PIN:
print("FAIL count headings moved %d -> %d" % (PIN, n))
print("CHECK 1 fault(s)")
sys.exit(1)
print("CHECK OK: counts match the pin")
sys.exit(0)
'''
def _fixture(base):
"""A seven-commit history with one commit of each kind. Returns (repo, revs).
A: queue only, no gate tool -> index UNGRADABLE, pin UNGRADABLE
B: appends, adds tools/queue_index.py + its render + the pin tool (PIN=1)
-> both PASS
C: appends to the queue, no re-render -> index STALE, pin PASS
D: appends AND re-renders -> both PASS
F: appends a HEADING and re-renders, pin left -> index PASS, pin DRIFT
G: appends a record and moves the pin to 2 -> both PASS
E: touches an unrelated file -> not selected at all
"""
repo = os.path.join(base, "repo")
@@ -402,15 +543,19 @@ def _fixture(base):
_sh(repo, "git", "add", "-A")
_sh(repo, "git", "commit", "-q", "-m", "A: queue only")
pin_dir = os.path.join(repo, os.path.dirname(PIN_TOOL))
os.makedirs(pin_dir)
os.makedirs(os.path.join(repo, "tools"))
os.makedirs(os.path.join(repo, "docs"))
with open(os.path.join(repo, PIN_TOOL), "w", encoding="utf-8") as fh:
fh.write(PIN_STUB.replace("@PIN@", "1"))
with open(os.path.join(repo, GATE_TOOL), "w", encoding="utf-8") as fh:
fh.write(open(os.path.join(HERE, "queue_index.py"), encoding="utf-8").read())
with open(queue, "a", encoding="utf-8") as fh:
fh.write("- beta appended\n")
_sh(repo, sys.executable, GATE_TOOL, "--repo", ".", "--write", INDEX_DEFAULT)
_sh(repo, "git", "add", "-A")
_sh(repo, "git", "commit", "-q", "-m", "B: append + gate + index")
_sh(repo, "git", "commit", "-q", "-m", "B: append + gate + pin + index")
with open(queue, "a", encoding="utf-8") as fh:
fh.write("- gamma appended\n")
@@ -423,6 +568,23 @@ def _fixture(base):
_sh(repo, "git", "add", "-A")
_sh(repo, "git", "commit", "-q", "-m", "D: append + re-render")
# F: the shape Q167 landed in -- the index is re-rendered and current, the
# pin is not moved, so the two axes disagree in ONE row.
with open(queue, "a", encoding="utf-8") as fh:
fh.write("\n## Q2 -- beta\n\nStatus: open\n")
_sh(repo, sys.executable, GATE_TOOL, "--repo", ".", "--write", INDEX_DEFAULT)
_sh(repo, "git", "add", "-A")
_sh(repo, "git", "commit", "-q", "-m", "F: new heading + re-render, pin left")
# G: the repair commit -- touches the queue and moves the pin with it.
with open(queue, "a", encoding="utf-8") as fh:
fh.write("- epsilon appended\n")
with open(os.path.join(repo, PIN_TOOL), "w", encoding="utf-8") as fh:
fh.write(PIN_STUB.replace("@PIN@", "2"))
_sh(repo, sys.executable, GATE_TOOL, "--repo", ".", "--write", INDEX_DEFAULT)
_sh(repo, "git", "add", "-A")
_sh(repo, "git", "commit", "-q", "-m", "G: append + move the pin")
with open(os.path.join(repo, "notes.txt"), "w", encoding="utf-8") as fh:
fh.write("unrelated\n")
_sh(repo, "git", "add", "-A")
@@ -440,12 +602,12 @@ def selftest():
base = tempfile.mkdtemp(prefix="qaa-selftest-")
try:
repo, revs = _fixture(base)
a, b, c, d, e = revs
a, b, c, d, f, g, e = revs
rc, p = audit(repo, QUEUE_DEFAULT, INDEX_DEFAULT, None, "HEAD",
all_history=True)
rows = {r["rev"]: r for r in p["rows"]}
check("the audit selects the 4 queue-touching commits, not the 5th", len(p["rows"]), 4)
check("the audit selects the 6 queue-touching commits, not the 7th", len(p["rows"]), 6)
check("a commit with no gate tool is UNGRADABLE, not STALE", rows[a]["verdict"], "UNGRADABLE")
check("the commit that added the gate and its index is PASS", rows[b]["verdict"], "PASS")
check("an append with no re-render is STALE", rows[c]["verdict"], "STALE")
@@ -455,20 +617,52 @@ def selftest():
check("the stale row's numstat is the append", (rows[c]["queue_add"], rows[c]["queue_del"]), (1, 0))
check("a range with a STALE row exits 1", rc, 1)
check("counts keep UNGRADABLE out of the STALE bucket", p["counts"].get("STALE"), 1)
check("graded counts PASS+STALE+ERROR only", p["graded"], 3)
check("graded counts PASS+STALE+ERROR only", p["graded"], 5)
check("the STALE detail carries the tool's own word", "STALE:" in rows[c]["detail"], True)
# -- the pin axis, measured in the same trees -----------------------
check("a commit that predates the pin tool is pin UNGRADABLE", rows[a]["pin"], "UNGRADABLE")
check("the commit that shipped the pin reads pin PASS", rows[b]["pin"], "PASS")
check("an index-stale commit whose pin matches reads pin PASS", rows[c]["pin"], "PASS")
check("a new heading with the pin left behind reads pin DRIFT", rows[f]["pin"], "DRIFT")
check("the DRIFT row's INDEX is still PASS -- the axes are independent",
rows[f]["verdict"], "PASS")
check("the DRIFT row names the count that moved",
"count headings moved 1 -> 2" in rows[f]["pin_detail"], True)
check("a queue-touching commit that moves the pin reads pin PASS", rows[g]["pin"], "PASS")
check("DRIFT is kept out of the pin PASS bucket", p["pin_counts"].get("DRIFT"), 1)
check("pin UNGRADABLE is counted, not folded into DRIFT", p["pin_counts"].get("UNGRADABLE"), 1)
check("pin_graded counts PASS+DRIFT+ERROR only", p["pin_graded"], 5)
check("a DRIFT row alone exits 1", rows[f]["pin_rc"], 1)
rc_p, p_p = audit(repo, QUEUE_DEFAULT, INDEX_DEFAULT, None, f, guard=True,
axis="pin")
check("--axis pin grades the pin alone and still exits 1 on DRIFT",
(rc_p, p_p["rows"][0]["pin"]), (1, "DRIFT"))
check("--axis pin sets no index verdict on the row",
"verdict" in p_p["rows"][0], False)
check("--axis pin counts no index rows", p_p["counts"], {})
rc_i, p_i = audit(repo, QUEUE_DEFAULT, INDEX_DEFAULT, None, c, guard=True,
axis="index")
check("--axis index reads the same commit STALE on its own axis",
(rc_i, p_i["rows"][0]["verdict"], "pin" in p_i["rows"][0]),
(1, "STALE", False))
rc_g, p_g = audit(repo, QUEUE_DEFAULT, INDEX_DEFAULT, None, "HEAD", guard=True)
check("--guard on a clean head exits 0", rc_g, 0)
check("--guard reports exactly one commit", len(p_g["rows"]), 1)
check("--guard's one row is clean on BOTH axes",
(p_g["rows"][0]["verdict"], p_g["rows"][0]["pin"]), ("PASS", "PASS"))
rc_s, _ = audit(repo, QUEUE_DEFAULT, INDEX_DEFAULT, None, c, guard=True)
check("--guard on the stale commit exits 1", rc_s, 1)
rc_f, _ = audit(repo, QUEUE_DEFAULT, INDEX_DEFAULT, None, f, guard=True)
check("--guard on the drift commit exits 1", rc_f, 1)
# Default range: begin where the gate does. B introduced the index, so
# the default table starts at B's parent and A is counted, not listed.
rc_a, p_a = audit(repo, QUEUE_DEFAULT, INDEX_DEFAULT, None, "HEAD")
check("the default range begins at the commit that introduced the index",
(p_a["auto_from"], len(p_a["rows"])), (True, 3))
(p_a["auto_from"], len(p_a["rows"])), (True, 5))
check("pre-gate commits are counted, not silently dropped", p_a["pre_gate"], 1)
check("the default range still grades the stale commit", rc_a, 1)
@@ -491,8 +685,9 @@ def selftest():
report(p_j, stream=buf)
with open(json_path, encoding="utf-8") as fh:
loaded = json.load(fh)
check("--json round-trips the same 4 rows", len(loaded["rows"]), 4)
check("--json round-trips the same 6 rows", len(loaded["rows"]), 6)
check("the report names the stale commit by short sha", c[:7] in buf.getvalue(), True)
check("the report names the drift commit on its own line", "drift: " + f[:7] in buf.getvalue(), True)
check("a report with a fault never prints a clean verdict",
"every graded commit" in buf.getvalue(), False)
finally:
@@ -524,6 +719,9 @@ def main(argv=None):
"commits as UNGRADABLE rows too")
ap.add_argument("--guard", action="store_true",
help="grade only the newest queue-touching commit")
ap.add_argument("--axis", choices=("both", "index", "pin"), default="both",
help="which axis to grade: the rendered index, the queue's "
"own PIN_COUNTS, or both (default both)")
ap.add_argument("--timeout", type=int, default=30,
help="seconds per commit's --check (default 30)")
ap.add_argument("--json", metavar="PATH", help="write the rows here")
@@ -533,7 +731,7 @@ def main(argv=None):
return selftest()
rc, payload = audit(args.repo, args.queue, args.index,
args.rev_from, args.rev_to, args.guard, args.timeout,
args.all_history)
args.all_history, args.axis)
report(payload)
if args.json:
try: