Closure of Q71 (ledger's commits-table.md).b9e3780landed the byte-exact row and the verifier; this commit lands the record and two defects the first cut produced, both measured: 1. The pin was not reproducible. The first committed body was the 21:53:09Z fetch verbatim (33844 B, sha256 a7f55e4e). --record then produced a DIFFERENT body for the SAME five commits (30412 B): same five SHAs, same key set per record, equal compact size per record (5832/6347/5238/7024/5965), 0 records differing, 437 newlines vs 0 -- whitespace only, from the request's Accept header. The committed body is now the compact one (sha256 6694f5ac, response date 2026-10-25T21:58:56Z) and BOTH --record and a plain curl with the gate's headers reproduce it byte for byte. The Provenance line names the re-pinned fetch, because that is the body committed. 2. --record wrote the body it graded from, i.e. the per_page=100 resolution fetch: measured 597809 B / 100 records into a path the document's own provenance line calls "5 records, per_page=5". Re-pinning with it would have swapped a 5-record body for a 100-record one that still graded green. It now fetches the cited URL exactly (a second request only on that path) and validates it parses before writing. Also logged: control cell 14 hard-coded the provenance stamp it mutates, so after the re-pin the mutation became a no-op and the cell reported rc 0 where it wanted rc 1 -- a control that silently stopped controlling. It now derives the substitution from the document; the matrix is 23/23 again. Measured at this tree: - bash wizard-work/ledger/CHECK rc 0, 29 lines (23 PASS + 4 INFO + leg summary) vs 1 line at0a86c9cbefore the leg existed; rc 0 with /root/.hermes masked (unshare -m + tmpfs, 0 entries). - Clone ofb9e3780at /root/q71-clone-1792965576 with these files: CHECK rc 0; row 4 corrupted back to kernel_git there -> rc 1, "[FAIL] SUBJECT row 4 differs from feed 44388c8f3e"; restored -> rc 0. The leg proves itself inside CHECK, not only under its own harness. - git archive export ofb9e3780(no .git): verifier rc 0 pinned and rc 0 live. - 23/23 control cells (scratch/q71/controls.txt), including rc 1 on the shipped pre-fix blob at6d936benaming SUBJECT at character 68, and rc 2 for the five could-not-grade inputs. - python3 tools/verify.py --repo . -> 8 legs, 8 pass, 0 fault, 0 ungradable. Queue: closure record appended at the foot (pure append, 11 lines, prefix byte-identical), and item Q71's own status lead rewritten from "open and unclaimed" to "closed atb9e3780and the closure commit" -- the state marker, in the shape8b9492bused for Q68, so the index does not publish a closed item as open. queue_claim_check: Q71 out of the open set; docs/queue-index.md re-rendered in this commit. Evidence: wizard-work/volt/q71-2026-10-25.md.
365 lines
15 KiB
Python
365 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
"""Verification for `commits-table.md` (ledger / Extract lane).
|
|
|
|
The table names its own source on a `Source:` line. This gate re-reads that
|
|
line, fetches *that* endpoint, and grades every row against the response body
|
|
the endpoint returns now -- so the artifact is tied to its data by a
|
|
measurement rather than by the sentence that claims it.
|
|
|
|
What is graded (rc 1 on any failure):
|
|
|
|
TABLE the document parses: a `Source:` URL, a `Rows: N` claim, a four
|
|
column header over N body rows, 4 cells per row, no unescaped
|
|
pipe (which is how a markdown table silently loses a cell).
|
|
FEED the response body is a JSON array of commit objects.
|
|
RESOLVE every row's SHA prefix is >= 7 lowercase hex characters and is a
|
|
prefix of exactly one SHA in the feed (ambiguous -> fault).
|
|
SUBJECT the row's Subject equals the feed's first message line, byte for
|
|
byte. This is the leg that catches a hand-retyped URL inside a
|
|
merge subject -- one character, invisible to every reader who
|
|
does not re-fetch, indistinguishable from a fabricated path.
|
|
DATE the row's Date equals the feed's *author* date, day precision.
|
|
The column is day-precision by construction, so a within-day
|
|
author/committer difference is printed as an observation.
|
|
ORDER rows appear in feed order (row i's feed index strictly increases
|
|
with i), i.e. the table is most-recent-first as its title says.
|
|
PROVENANCE the `Provenance:` line names the same URL as `Source:`, a UTC
|
|
fetch timestamp, the record count the body rows imply, and the
|
|
per_page the URL asked for. Q62 leg (c)'s lesson: an artifact
|
|
that names its source is not the same as one that can be audited.
|
|
RECENCY OBSERVED by default, not graded: whether these rows are still the
|
|
N most recent commits at the endpoint. A committed file cannot be
|
|
abreast of a moving feed, and a leg that goes red for every reader
|
|
forever is a leg everyone learns to ignore. `--strict-recency`
|
|
promotes it to a graded leg for a periodic sweep.
|
|
|
|
`--feed PATH` grades against a retained response body instead of the network
|
|
(this is how the lane gate runs it: hermetic, no network in CHECK). The
|
|
`--record PATH` flag fetches the cited URL *exactly as the document writes it*
|
|
(a second request, only on this path) and writes that body to PATH, so the pin
|
|
it produces is the body the document's `per_page` and record count claim -- not
|
|
the wider `per_page=100` body used to resolve rows.
|
|
|
|
House exit contract (the same one `tools/` uses): 0 = every graded leg passed,
|
|
1 = a graded leg failed, 2 = could not grade (missing table, unparsable body,
|
|
transport failure). No asserts, so `-O` cannot delete a check.
|
|
|
|
Every path is resolved from `__file__`; nothing here names the author's
|
|
checkout, so the gate grades the commit it sits in and not the tree it was
|
|
written in.
|
|
"""
|
|
import json
|
|
import re
|
|
import sys
|
|
import urllib.error
|
|
import urllib.parse
|
|
import urllib.request
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import NoReturn, Optional
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
TABLE = HERE / "commits-table.md"
|
|
COLUMNS = ["#", "SHA", "Date", "Subject"]
|
|
MIN_PREFIX = 7
|
|
# Resolution depth. The artifact cites per_page=5; asking for 100 only widens
|
|
# the window rows are resolved in, so a row that has since been pushed down the
|
|
# feed is still identifiable instead of looking invented.
|
|
RESOLVE_DEPTH = 100
|
|
TIMEOUT = 40
|
|
|
|
GRADED = []
|
|
|
|
|
|
def say(ok, leg, detail=""):
|
|
print("[%s] %-10s %s" % ("PASS" if ok else "FAIL", leg, detail))
|
|
if not ok:
|
|
GRADED.append(leg)
|
|
|
|
|
|
def observe(leg, detail):
|
|
print("[INFO] %-10s %s" % (leg, detail))
|
|
|
|
|
|
def die(code, msg) -> NoReturn:
|
|
print("UNGRADABLE: %s" % msg)
|
|
sys.exit(code)
|
|
|
|
|
|
# ---------------------------------------------------------------- document --
|
|
|
|
def read_table(path):
|
|
if not path.is_file():
|
|
die(2, "no table at %s" % path)
|
|
try:
|
|
text = path.read_text(encoding="utf-8")
|
|
except (OSError, UnicodeDecodeError) as exc:
|
|
die(2, "cannot read %s: %s" % (path, exc))
|
|
return text
|
|
|
|
|
|
def parse_table(text):
|
|
"""Return dict(url, rows_claim, rows). Rows are 4-cell lists; a row whose
|
|
cell count is not 4 is kept (so it can be named) with `cells` recording it.
|
|
"""
|
|
url = None
|
|
for line in text.split("\n"):
|
|
m = re.match(r"^Source:\s*(\S+)\s*$", line)
|
|
if m:
|
|
url = m.group(1)
|
|
break
|
|
m = re.search(r"^Rows:\s*(\d+)\s*$", text, re.MULTILINE)
|
|
rows_claim = int(m.group(1)) if m else None
|
|
|
|
rows, header = [], None
|
|
for line in text.split("\n"):
|
|
s = line.strip()
|
|
if not s.startswith("|"):
|
|
continue
|
|
cells = [c.strip() for c in s[1:-1].split("|")] if s.endswith("|") else None
|
|
if cells is None:
|
|
continue
|
|
if header is None:
|
|
header = cells
|
|
continue
|
|
if all(re.fullmatch(r":?-{2,}:?", c) for c in cells):
|
|
continue # separator
|
|
if re.fullmatch(r"\d+", cells[0]):
|
|
rows.append(cells)
|
|
return {"url": url, "rows_claim": rows_claim, "header": header, "rows": rows}
|
|
|
|
|
|
def grade_document(doc):
|
|
say(doc["url"] is not None, "TABLE", "Source: %s" % doc["url"] if doc["url"] else "no `Source:` line")
|
|
if doc["url"]:
|
|
parsed = urllib.parse.urlsplit(doc["url"])
|
|
say(parsed.scheme == "https" and bool(parsed.netloc), "TABLE", "source is an https URL")
|
|
say(doc["header"] == COLUMNS, "TABLE", "header %r" % (doc["header"],))
|
|
say(doc["rows_claim"] is not None, "TABLE", "Rows: %s" % doc["rows_claim"])
|
|
say(doc["rows_claim"] == len(doc["rows"]), "TABLE",
|
|
"Rows: %s vs %d body rows" % (doc["rows_claim"], len(doc["rows"])))
|
|
bad = [(r[0], len(r)) for r in doc["rows"] if len(r) != len(COLUMNS)]
|
|
say(not bad, "TABLE", "cell counts %s" % (bad or "all %d" % len(COLUMNS)))
|
|
say(len(doc["rows"]) > 0, "TABLE", "%d data row(s)" % len(doc["rows"]))
|
|
|
|
|
|
# ------------------------------------------------------------------- feed ---
|
|
|
|
def fetch(url, depth: Optional[int] = RESOLVE_DEPTH):
|
|
"""GET `url`. depth=None requests the URL exactly as the document cites it;
|
|
otherwise per_page is overridden to `depth` for row resolution only."""
|
|
if depth is None:
|
|
target = url
|
|
elif "per_page=" in url:
|
|
target = re.sub(r"per_page=\d+", "per_page=%d" % depth, url)
|
|
else:
|
|
target = "%s%sper_page=%d" % (url, "&" if "?" in url else "?", depth)
|
|
req = urllib.request.Request(target, headers={
|
|
"User-Agent": "ledger-commits-table-gate/1.0",
|
|
"Accept": "application/vnd.github+json",
|
|
})
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=TIMEOUT) as r:
|
|
body = r.read()
|
|
status = r.status
|
|
except urllib.error.HTTPError as exc:
|
|
die(2, "HTTP %s from %s" % (exc.code, target))
|
|
except (urllib.error.URLError, OSError) as exc:
|
|
die(2, "transport failure for %s: %s" % (target, exc))
|
|
if status != 200:
|
|
die(2, "HTTP %s from %s" % (status, target))
|
|
observe("FEED", "fetched %s (%d bytes%s)" % (
|
|
target, len(body), ", per_page=%d" % depth if depth is not None else ", as cited"))
|
|
return body, target
|
|
|
|
|
|
def load_feed(body, origin):
|
|
try:
|
|
feed = json.loads(body)
|
|
except (ValueError, UnicodeDecodeError) as exc:
|
|
die(2, "response body is not JSON (%s): %s" % (origin, exc))
|
|
if not isinstance(feed, list) or not feed:
|
|
die(2, "response body is not a non-empty JSON array (%s)" % origin)
|
|
for i, rec in enumerate(feed):
|
|
try:
|
|
rec["sha"], rec["commit"]["message"], rec["commit"]["author"]["date"]
|
|
except (KeyError, TypeError):
|
|
die(2, "record %d lacks sha/commit.message/commit.author.date (%s)" % (i, origin))
|
|
return feed
|
|
|
|
|
|
# ------------------------------------------------------------------ grading --
|
|
|
|
def record_view(rec):
|
|
msg = rec["commit"]["message"]
|
|
subject = msg.split("\n", 1)[0]
|
|
author = rec["commit"]["author"]["date"]
|
|
committer = rec["commit"].get("committer", {}).get("date", author)
|
|
return {"sha": rec["sha"], "subject": subject, "author": author, "committer": committer}
|
|
|
|
|
|
def grade_rows(doc, feed):
|
|
views = [record_view(r) for r in feed]
|
|
index_of = {}
|
|
for i, v in enumerate(views):
|
|
index_of.setdefault(v["sha"], i)
|
|
|
|
resolved = []
|
|
for row in doc["rows"]:
|
|
num, prefix = row[0], row[1]
|
|
if not re.fullmatch(r"[0-9a-f]{%d,}" % MIN_PREFIX, prefix):
|
|
say(False, "RESOLVE", "row %s: %r is not a >=%d-char lowercase hex prefix" % (num, prefix, MIN_PREFIX))
|
|
resolved.append(None)
|
|
continue
|
|
hits = [i for i, v in enumerate(views) if v["sha"].startswith(prefix)]
|
|
if len(hits) != 1:
|
|
say(False, "RESOLVE", "row %s: %r matches %d feed SHAs (need exactly 1)" % (num, prefix, len(hits)))
|
|
resolved.append(None)
|
|
continue
|
|
resolved.append(hits[0])
|
|
ok = [r for r in resolved if r is not None]
|
|
say(len(ok) == len(resolved), "RESOLVE",
|
|
"%d/%d rows resolve to exactly one feed SHA" % (len(ok), len(resolved)))
|
|
|
|
for row, idx in zip(doc["rows"], resolved):
|
|
if idx is None:
|
|
continue
|
|
v = views[idx]
|
|
num, _, date, subject = row[0], row[1], row[2], row[3]
|
|
if subject != v["subject"]:
|
|
say(False, "SUBJECT", "row %s differs from feed %s" % (num, v["sha"][:10]))
|
|
if len(subject) == len(v["subject"]):
|
|
k = next((j for j in range(len(subject)) if subject[j] != v["subject"][j]), None)
|
|
if k is not None:
|
|
print(" first difference at character %d" % k)
|
|
print(" table: ...%s..." % subject[max(0, k - 24):k + 25])
|
|
print(" feed : ...%s..." % v["subject"][max(0, k - 24):k + 25])
|
|
else:
|
|
print(" table: %r" % subject)
|
|
print(" feed : %r" % v["subject"])
|
|
continue
|
|
say(True, "SUBJECT", "row %s == %s" % (num, v["sha"][:10]))
|
|
day = v["author"][:10]
|
|
say(date == day, "DATE", "row %s: table %s vs feed author %s" % (num, date, v["author"]))
|
|
if v["committer"][:10] != v["author"][:10]:
|
|
observe("DATE", "row %s author %s and committer %s fall on different days" % (num, v["author"], v["committer"]))
|
|
|
|
seq = [r for r in resolved if r is not None]
|
|
say(seq == sorted(set(seq)) and len(seq) == len(set(seq)), "ORDER",
|
|
"row order vs feed index %s" % (seq,))
|
|
return resolved, views
|
|
|
|
|
|
def grade_provenance(text, doc):
|
|
m = re.search(r"^Provenance:\s+(\S+)\s+fetched\s+(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)"
|
|
r"\s+\((\d+)\s+records?,\s*per_page=(\d+)\)\s*$", text, re.MULTILINE)
|
|
if not m:
|
|
say(False, "PROVENANCE", "no `Provenance: <url> fetched <UTC> (<n> records, per_page=<k>)` line")
|
|
return
|
|
url, stamp, records, per_page = m.group(1), m.group(2), int(m.group(3)), int(m.group(4))
|
|
say(url == doc["url"], "PROVENANCE", "names the same endpoint as Source: (%s)" % url)
|
|
try:
|
|
datetime.strptime(stamp, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
|
|
parsed = True
|
|
except ValueError:
|
|
parsed = False
|
|
say(parsed, "PROVENANCE", "fetch timestamp %s parses as UTC" % stamp)
|
|
say(records == len(doc["rows"]), "PROVENANCE",
|
|
"%d records claimed for %d body rows" % (records, len(doc["rows"])))
|
|
m2 = re.search(r"per_page=(\d+)", doc["url"] or "")
|
|
say(bool(m2) and per_page == int(m2.group(1)), "PROVENANCE",
|
|
"per_page=%d matches the Source URL's per_page=%s" % (per_page, m2.group(1) if m2 else "absent"))
|
|
observe("PROVENANCE", "captured %s" % stamp if parsed else "captured (unparsed)")
|
|
|
|
|
|
def grade_recency(doc, resolved, strict):
|
|
n = len(doc["rows"])
|
|
top = list(range(n))
|
|
seq = [r for r in resolved if r is not None]
|
|
if seq == top:
|
|
observe("RECENCY", "rows are the %d most recent records at the endpoint" % n)
|
|
if strict:
|
|
say(True, "RECENCY", "rows are still the top %d" % n)
|
|
return
|
|
if strict:
|
|
say(False, "RECENCY", "rows are not the top %d (%s)" % (n, seq))
|
|
else:
|
|
newest = seq[0] if seq else None
|
|
observe("RECENCY", "rows are not the top %d at this reading (row 1 sits at feed index %s, "
|
|
"so %s commit(s) have landed since capture); not graded, "
|
|
"pass --strict-recency to grade it"
|
|
% (n, newest, newest))
|
|
|
|
|
|
# --------------------------------------------------------------------- main --
|
|
|
|
def main(argv):
|
|
table_path, feed_path, record_path = TABLE, None, None
|
|
strict = False
|
|
i = 0
|
|
while i < len(argv):
|
|
a = argv[i]
|
|
if a == "--table" and i + 1 < len(argv):
|
|
table_path = Path(argv[i + 1]).expanduser()
|
|
i += 2
|
|
elif a == "--feed" and i + 1 < len(argv):
|
|
feed_path = Path(argv[i + 1]).expanduser()
|
|
i += 2
|
|
elif a == "--record" and i + 1 < len(argv):
|
|
record_path = Path(argv[i + 1]).expanduser()
|
|
i += 2
|
|
elif a == "--strict-recency":
|
|
strict = True
|
|
i += 1
|
|
elif a in ("-h", "--help"):
|
|
print(__doc__)
|
|
return 0
|
|
else:
|
|
print("unknown argument: %r" % a, file=sys.stderr)
|
|
return 2
|
|
|
|
text = read_table(table_path)
|
|
doc = parse_table(text)
|
|
grade_document(doc)
|
|
if not doc["url"]:
|
|
die(2, "the document names no source, so there is nothing to fetch")
|
|
|
|
if feed_path is not None:
|
|
if not feed_path.is_file():
|
|
die(2, "no retained feed body at %s" % feed_path)
|
|
try:
|
|
body = feed_path.read_bytes()
|
|
except OSError as exc:
|
|
die(2, "cannot read %s: %s" % (feed_path, exc))
|
|
origin = "retained body %s" % feed_path.name
|
|
observe("FEED", "%s (%d bytes)" % (origin, len(body)))
|
|
else:
|
|
if record_path is not None:
|
|
cited, cited_url = fetch(doc["url"], depth=None)
|
|
load_feed(cited, "cited endpoint") # never pin a rate-limit page
|
|
try:
|
|
record_path.write_bytes(cited)
|
|
observe("FEED", "recorded %d bytes of %s to %s (the cited URL exactly, "
|
|
"so the pin matches what the document claims)"
|
|
% (len(cited), cited_url, record_path))
|
|
except OSError as exc:
|
|
die(2, "cannot write %s: %s" % (record_path, exc))
|
|
body, origin = fetch(doc["url"])
|
|
|
|
feed = load_feed(body, origin)
|
|
observe("FEED", "%d record(s) in %s" % (len(feed), origin))
|
|
resolved, views = grade_rows(doc, feed)
|
|
grade_provenance(text, doc)
|
|
grade_recency(doc, resolved, strict)
|
|
|
|
if GRADED:
|
|
print("TABLE FAIL: %s" % ", ".join(GRADED))
|
|
return 1
|
|
print("TABLE PASS: %d row(s) reproduce from %s byte for byte."
|
|
% (len(doc["rows"]), "the retained body" if feed_path else "a live fetch"))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv[1:]))
|