302 lines
14 KiB
Python
302 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
"""Q60 enforcement check (Zeph lane, 2026-10-26).
|
|
|
|
Implements the narrowed Q60 residue from reply 100 section 6 and the
|
|
WORK-QUEUE.md Q60 addendum: the census half is landed; this is the check half.
|
|
|
|
What it does, on a checkout at a NAMED head:
|
|
1. Population: count truncated-identifier occurrences / distinct tokens /
|
|
files under the pinned pattern ``[0-9a-f]{8,}(-[0-9a-f]{4})*-…``.
|
|
The head and the exclusion list are printed beside every number, because
|
|
a recount at the head that carries the report re-counts the report itself
|
|
(reply 100 section 2: 77/19 at 0d294b8 collapses to 49/17 once the census
|
|
file and the check-in that quotes it are excluded). Occurrence and
|
|
distinct counts are printed side by side, because the 31 -> 35 -> 16
|
|
drift was an occurrence/distinct confusion and the distinct count was
|
|
constant at 16 (reply 100 section 3).
|
|
2. Recovery: for each distinct truncated stem, look for a longer string in
|
|
the tree (same exclusions) beginning with that stem. Stems with no
|
|
in-tree longer string fall back to the KNOWN tables below, which are
|
|
census rows (in-tree recoveries) and reply-100 section 4 (external
|
|
recoveries). A stem in neither place FAILS the gate: it is a new
|
|
unrecovered identifier-subject token.
|
|
3. Complete-but-wrong: any full UUID-shaped value in the tree whose first
|
|
8 hex chars match a KNOWN full value's prefix but whose value differs
|
|
FAILS the gate. The known invented placeholder for beae0b1f is excluded
|
|
from this leg by construction: that prefix has no KNOWN full value.
|
|
Two qualifications from reply 113 / interview 117: (a) the selftest's
|
|
own fixture value classifies TEST-FIXTURE, not FAIL, and is built by
|
|
concatenation so no literal for it exists in this source; (b) FAIL
|
|
diagnostics are masked (stem + length + site, never the full value),
|
|
because a report quoting the fault re-commits it at the next head.
|
|
The beae0b1f stem rule keys on the 8-char stem: any future token
|
|
sharing that stem classifies exhausted without a look (reply 113
|
|
section 1 boundary note).
|
|
|
|
Quoting rule (interview 117 Q2): a verdict is only ever quoted at a head
|
|
that contains the artifact it grades. Green readings at heads predating
|
|
this file are census readings, not evidence for the check.
|
|
|
|
Exit status: 0 when no FAIL line is printed, 1 otherwise.
|
|
``--selftest`` builds fixture trees and asserts the negative cells: an
|
|
unknown truncated token fails, a real complete-but-wrong value fails, the
|
|
fixture value classifies TEST-FIXTURE, a self-quoting report file excluded
|
|
from the population collapses the count, and the check over the checker's
|
|
own directory passes (cell 7, the self-scan leg).
|
|
|
|
Usage:
|
|
python3 q60_enforce.py --tree <checkout-path> [--exclude <relpath> ...]
|
|
python3 q60_enforce.py --selftest
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
TRUNC_RE = re.compile("([0-9a-f]{8,}(?:-[0-9a-f]{4})*)-\u2026")
|
|
FULL_RE = re.compile(
|
|
r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
|
|
)
|
|
# Longer-string search: a UUID-shaped continuation of the stem (one or more
|
|
# hyphen groups of 4-12 hex, so the 8-4-4-4-12 shape matches in full).
|
|
# The truncated form itself (stem + hyphen, before the ellipsis) must NOT
|
|
# count: a bare "stem-" is not a longer string.
|
|
LONG_RE_TEMPLATE = r"{stem}(-[0-9a-f]{{4,12}})+"
|
|
|
|
# Census in-tree recoveries (recoveries/truncated-identifier-census.md rows
|
|
# 2, 3, 4, 6, 7, 8, 10, 13, 14, 15), keyed by first 8 hex chars.
|
|
KNOWN_IN_TREE = {
|
|
"3d79ba82": "3d79ba82-926b-48c0-9d15-8791a364ea81",
|
|
"34413f8d": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
|
|
"a92b55d0": "a92b55d0-1d36-4884-8e65-f456450b3a74",
|
|
"3c67f712": "3c67f712-697d-48d8-b05c-01be896e61da",
|
|
"29ba0d51": "29ba0d51-2d9f-412e-ad19-5f28a76ca96e",
|
|
"e33f87f8": "e33f87f8-88ec-4e1a-9a13-df9bbb1d8120",
|
|
"6ec57334": "6ec57334-604d-42b5-a2e7-54662739c504",
|
|
"51a65c2c": "51a65c2c-07dd-4307-aba6-f7c4af17311c",
|
|
"20c08939": "20c08939-56c2-4351-a47a-efcdf022eca5",
|
|
"18a809aa": "18a809aa-0b7f-4523-ac0e-508c9f0f6f66",
|
|
}
|
|
|
|
# External recoveries (reply 100 section 4, Timmy's fetches, 2026-10-25).
|
|
KNOWN_EXTERNAL = {
|
|
"6b5089a8": "6b5089a8-2daf-4b4e-a739-bcf762cfe29a",
|
|
"10dfc8bc": "10dfc8bc-99ad-4ca2-ab76-853cb90a92c2",
|
|
"a90cdb7c": "a90cdb7c-1c33-4fca-9a96-26c236006f14",
|
|
"6c573a9c": "6c573a9c-1d6f-4ae6-a93e-ff79868456f4",
|
|
"67cbefc2": "67cbefc2-d94f-4f31-9e6e-b6210a60ad5e",
|
|
}
|
|
|
|
KNOWN_FULL = dict(KNOWN_IN_TREE)
|
|
KNOWN_FULL.update(KNOWN_EXTERNAL)
|
|
|
|
# Test fixture, not an archive claim (reply 113 / interview 117 section 0.7):
|
|
# the complete-but-wrong value used by selftest cell 4's predecessor. It is
|
|
# built by concatenation on purpose: no UUID-shaped literal for this value
|
|
# may exist in this source, or the file would fail its own leg (the fault
|
|
# that kept Q60 open at 36c6bba). Occurrences classify TEST-FIXTURE and do
|
|
# not fail -- but the line is still printed with its site, so a re-commit
|
|
# of the same value elsewhere stays visible in the report rather than
|
|
# silent. Value alone cannot distinguish fixture from re-commit; site can.
|
|
KNOWN_FIXTURE = {
|
|
"3d79ba82": "3d79ba82-" "1111-2222-3333-444444444444",
|
|
}
|
|
|
|
# Terminal justified-unrecovered state (reply 100 section 4): exhausted by
|
|
# full-history pickaxe, and the only longer string anywhere is Timmy's
|
|
# invented placeholder, which is not a recovery and must not count as one.
|
|
KNOWN_UNRECOVERED = {"beae0b1f"}
|
|
PLACEHOLDER = "beae0b1f-0000-0000-0000-000000000000"
|
|
|
|
|
|
def list_files(tree):
|
|
"""Tracked files if tree is a git checkout, else every file under it."""
|
|
git_dir = os.path.join(tree, ".git")
|
|
if os.path.isdir(git_dir) or os.path.isfile(git_dir):
|
|
out = subprocess.run(
|
|
["git", "-C", tree, "ls-files"],
|
|
capture_output=True, text=True, check=True,
|
|
)
|
|
return [p for p in out.stdout.splitlines() if p]
|
|
found = []
|
|
for root, _dirs, names in os.walk(tree):
|
|
for name in names:
|
|
found.append(os.path.relpath(os.path.join(root, name), tree))
|
|
return sorted(found)
|
|
|
|
|
|
def read_text(path):
|
|
with open(path, "r", encoding="utf-8", errors="replace") as fh:
|
|
return fh.read()
|
|
|
|
|
|
def head_of(tree):
|
|
try:
|
|
out = subprocess.run(
|
|
["git", "-C", tree, "rev-parse", "HEAD"],
|
|
capture_output=True, text=True, check=True,
|
|
)
|
|
return out.stdout.strip()
|
|
except Exception:
|
|
return "(not a git checkout)"
|
|
|
|
|
|
def run_check(tree, excludes):
|
|
"""Return (report_lines, failed_bool). Pure function over the tree."""
|
|
lines = []
|
|
failed = False
|
|
head = head_of(tree)
|
|
lines.append("HEAD=%s" % head)
|
|
lines.append("EXCLUDE=%s" % ",".join(excludes) if excludes else "EXCLUDE=(none)")
|
|
files = [f for f in list_files(tree) if f not in set(excludes)]
|
|
contents = {}
|
|
for rel in files:
|
|
try:
|
|
contents[rel] = read_text(os.path.join(tree, rel))
|
|
except OSError:
|
|
continue
|
|
occ = 0
|
|
per_token = {}
|
|
files_with = set()
|
|
for rel, text in contents.items():
|
|
for m in TRUNC_RE.finditer(text):
|
|
occ += 1
|
|
per_token.setdefault(m.group(1), []).append(rel)
|
|
files_with.add(rel)
|
|
lines.append("OCC=%d DISTINCT=%d FILES=%d" % (occ, len(per_token), len(files_with)))
|
|
# Recovery + complete-but-wrong, per distinct stem.
|
|
for stem in sorted(per_token):
|
|
stem8 = stem[:8]
|
|
longer = None
|
|
if stem8 not in KNOWN_UNRECOVERED:
|
|
pat = re.compile(LONG_RE_TEMPLATE.format(stem=re.escape(stem)))
|
|
for rel, text in contents.items():
|
|
for m in pat.finditer(text):
|
|
cand = m.group(0)
|
|
# The placeholder and the test fixture are not
|
|
# recoveries: skip both, or the recovery line would
|
|
# name the fixture value as a recovery.
|
|
if cand != PLACEHOLDER and cand not in KNOWN_FIXTURE.values():
|
|
longer = (cand, rel)
|
|
break
|
|
if longer:
|
|
break
|
|
if longer:
|
|
lines.append("TOKEN %s: RECOVERED-IN-TREE %s at %s" % (stem, longer[0], longer[1]))
|
|
elif stem8 in KNOWN_EXTERNAL:
|
|
lines.append("TOKEN %s: RECOVERED-EXTERNAL %s (reply 100 s4)" % (stem, KNOWN_EXTERNAL[stem8]))
|
|
elif stem8 in KNOWN_IN_TREE:
|
|
lines.append("TOKEN %s: RECOVERED-IN-TREE-KNOWN %s" % (stem, KNOWN_IN_TREE[stem8]))
|
|
elif stem8 in KNOWN_UNRECOVERED:
|
|
lines.append("TOKEN %s: UNRECOVERED-JUSTIFIED (exhausted; placeholder is not a recovery)" % stem)
|
|
else:
|
|
lines.append("FAIL TOKEN %s: no longer string in tree, not in known tables" % stem)
|
|
failed = True
|
|
# Complete-but-wrong leg: full values whose 8-prefix is known.
|
|
# Diagnostics are MASKED (interview 117 section 0.7): the offending
|
|
# value is never printed in full, because every report that quotes the
|
|
# fault re-commits the fault at the next head. Stem + length + site is
|
|
# what a reader needs to locate it; the known value is already in the
|
|
# tables above, so printing it adds no new value to the record.
|
|
for rel, text in contents.items():
|
|
for val in set(FULL_RE.findall(text)):
|
|
prefix = val[:8]
|
|
if prefix in KNOWN_FULL and val != KNOWN_FULL[prefix]:
|
|
if val == KNOWN_FIXTURE.get(prefix):
|
|
lines.append("TEST-FIXTURE %s-\u2026 (len %d) at %s" % (prefix, len(val), rel))
|
|
else:
|
|
lines.append("FAIL COMPLETE-BUT-WRONG %s-\u2026 (len %d) at %s (known %s)"
|
|
% (prefix, len(val), rel, KNOWN_FULL[prefix]))
|
|
failed = True
|
|
lines.append("VERDICT=%s" % ("FAIL" if failed else "PASS"))
|
|
return lines, failed
|
|
|
|
|
|
def selftest():
|
|
"""Fixture trees; each cell asserts a behaviour, not a tautology."""
|
|
passed = []
|
|
|
|
def check(tree, excludes, want_fail, label):
|
|
_lines, failed = run_check(tree, excludes)
|
|
assert failed == want_fail, "%s: want fail=%s" % (label, want_fail)
|
|
passed.append(label)
|
|
|
|
with tempfile.TemporaryDirectory() as d:
|
|
# Cell 1: clean tree passes (one harmless abbreviation + its full value).
|
|
# Positive control, not a tautology (interview 117 Q3): a scanner
|
|
# returning [] for every input would pass a bare rc assertion, so
|
|
# the cell also asserts the RECOVERED-IN-TREE line is present.
|
|
with open(os.path.join(d, "a.md"), "w") as fh:
|
|
fh.write("see 3d79ba82-\u2026 and full 3d79ba82-926b-48c0-9d15-8791a364ea81\n")
|
|
lines, _ = run_check(d, [])
|
|
assert any(l.startswith("TOKEN 3d79ba82: RECOVERED-IN-TREE") for l in lines), \
|
|
"clean tree missing its recovery line (gutted scanner would pass rc alone)"
|
|
check(d, [], False, "clean-tree-passes")
|
|
# Cell 2: a self-quoting report excluded collapses the count.
|
|
with open(os.path.join(d, "report.md"), "w") as fh:
|
|
fh.write("3d79ba82-\u2026 3d79ba82-\u2026 3d79ba82-\u2026\n")
|
|
lines, _ = run_check(d, [])
|
|
assert any(l.startswith("OCC=4 ") for l in lines), "self-quote counted without exclusion"
|
|
lines, _ = run_check(d, ["report.md"])
|
|
assert any(l.startswith("OCC=1 ") for l in lines), "exclusion did not collapse count"
|
|
passed.append("self-quotation-excluded")
|
|
# Cell 3: unknown truncated token fails.
|
|
with open(os.path.join(d, "b.md"), "w") as fh:
|
|
fh.write("dead link deadbeef-\u2026 with no longer string\n")
|
|
check(d, ["report.md"], True, "unknown-token-fails")
|
|
os.remove(os.path.join(d, "b.md"))
|
|
# Cell 4: a REAL complete-but-wrong value fails. It must not be the
|
|
# fixture value (cell 6 owns that), and it is built by concatenation
|
|
# so no second UUID-shaped literal enters this source.
|
|
with open(os.path.join(d, "c.md"), "w") as fh:
|
|
fh.write("34413f8d-" "0000-0000-0000-000000000000\n")
|
|
check(d, ["report.md"], True, "complete-but-wrong-fails")
|
|
os.remove(os.path.join(d, "c.md"))
|
|
# Cell 5: justified-unrecovered placeholder does not count as recovery
|
|
# and does not fail.
|
|
with open(os.path.join(d, "e.md"), "w") as fh:
|
|
fh.write("beae0b1f-\u2026 and placeholder beae0b1f-0000-0000-0000-000000000000\n")
|
|
check(d, ["report.md"], False, "placeholder-not-a-recovery")
|
|
# Cell 6: the fixture value itself does NOT fail, and its line says
|
|
# TEST-FIXTURE with the site (auditable, not silent).
|
|
with open(os.path.join(d, "f.md"), "w") as fh:
|
|
fh.write("3d79ba82-" "1111-2222-3333-444444444444\n")
|
|
lines, _ = run_check(d, ["report.md"])
|
|
assert any(l.startswith("TEST-FIXTURE 3d79ba82-") and l.endswith("at f.md")
|
|
for l in lines), "fixture value did not classify TEST-FIXTURE at its site"
|
|
check(d, ["report.md"], False, "fixture-value-passes")
|
|
os.remove(os.path.join(d, "f.md"))
|
|
# Cell 7 (the sixth cell of reply 113, renumbered by cell 6): run the
|
|
# check over the checker's own directory and assert rc 0. This is
|
|
# the leg that was missing: five cells graded fixture trees, none
|
|
# ran the check over the file's own tree.
|
|
own_dir = os.path.dirname(os.path.abspath(__file__))
|
|
lines, failed = run_check(own_dir, [])
|
|
assert not failed, "self-scan failed: %s" % [l for l in lines if l.startswith("FAIL")]
|
|
passed.append("self-scan-passes")
|
|
for label in passed:
|
|
print("SELFTEST PASS %s" % label)
|
|
print("SELFTEST: %d cells passed, 0 failed" % len(passed))
|
|
return 0
|
|
|
|
|
|
def main(argv=None):
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--tree", default=".")
|
|
ap.add_argument("--exclude", action="append", default=[])
|
|
ap.add_argument("--selftest", action="store_true")
|
|
args = ap.parse_args(argv)
|
|
if args.selftest:
|
|
return selftest()
|
|
lines, failed = run_check(args.tree, args.exclude)
|
|
for line in lines:
|
|
print(line)
|
|
return 1 if failed else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|