#!/usr/bin/env python3 """Prior-art search over commit history — PENDING-164 options (c) and (d). WHY THIS EXISTS On 2026-06-05 the steward adopted Git LFS in chamber-library, found it a misfit, retired it, and rewrote seventeen commits to undo it (0677e8a). A per-repo hook exemption replaced it (400c054). Neither decision appears anywhere in PENDING.md, PENDING-archive.md or REVIEWED.md. Twelve weeks later the executor filed PENDING-163 recommending a route toward LFS, and the jurist recommended adopting it outright. Three exchanges were spent before the refutation surfaced, and it surfaced by accident — the string 'pre-lfs-export' appeared in an unrelated directory listing. The symptom was two parties reasoning toward a retired mechanism. The disease is that the record where such a decision is supposed to be findable does not contain it, so the only party who could have found it is the one with a filesystem. That is the asymmetry the three-party model exists to work around. WHAT IT DOES (c) Makes the substrate REACHABLE rather than copying it: searches commit messages across the steward's repos, unbounded by any commit-count window, and reports whether the same term appears in the authorization register. Exposed to the jurist through governance-mcp.py, whose repo_activity caps at 100 commits — a floor that sat five weeks short of 0677e8a. (d) Gives the executor a pre-proposal check: before any [PROPOSAL] naming a mechanism by name, run this and report the result. Mechanizes the discipline on the side that has the filesystem. --census runs the one-time backward sweep (PENDING-164 AMENDMENT 1): adoption and retirement verbs across every owned repo, each hit checked against the register. THE FINDING THIS LOOKS FOR Not "were there commits". The asymmetry: commits mention it, the register does not. That is a decision taken in a repo and never routed into the authorization record. ENUMERATION IS COMPUTED, NOT HAND-HELD Owned repos are those whose remotes point at the steward's hosts. A hand-maintained list is the failure mode PENDING-108 already measured: it is correct until someone forgets, and nothing reports the forgetting. Note the positive control below forced this: 0677e8a is in chamber-library and 95760ff is in dotfiles, and dotfiles is not in governance-mcp.py's REPOS. """ import re import subprocess import sys from pathlib import Path HOME = Path.home() OWNED_HOSTS = ("github.com/davidglidden", "davidglidden/", "git.skemantix.com") REGISTER = [HOME / "PENDING.md", HOME / "PENDING-archive.md", HOME / "REVIEWED.md"] # The census's verbs. Deliberately about ADOPTION and RETIREMENT of mechanisms, not about # ordinary change — "fix", "update" and "refactor" would return everything and measure # nothing. # ⚠ NARROWED after the first run, and the number is recorded because it is the finding. # The first verb set — which included "remove", "replace with", "no longer", "switch to" — # returned 661 candidates across 8 repos. That is a haystack, not a census: those verbs # catch ordinary development. Narrowed to forms that are almost always ABOUT A MECHANISM # rather than about a file. # # ⚠ AND THE HONEST LIMIT, stated here rather than discovered later: the jurist's # specification has two halves — grep the verbs, THEN check each hit against the register. # The first half is mechanical and is what this does. The second requires reading each # commit to identify WHICH mechanism it decided about, and that is interpretation, not # extraction. This instrument does not do it and does not pretend to. Same shape as # PENDING-151 step 1, where propositions could not be extracted mechanically either. CENSUS_VERBS = ["retire", "retired", "retiring", "abandon", "deprecat", "migrat", "stop using", "move away from", "back out", "revert to", "no longer use", "no longer using", "roll back", "un-adopt"] def sh(cmd, cwd=None): """stdout REGARDLESS of exit code. ⚠ Earned, not stylistic. The first form returned "" on a non-zero exit. `find` over $HOME exits 1 because 154 directories under Library are unreadable — while printing all 37 repos to stdout. So the repo list came back empty, every search returned zero, and the instrument would have reported "no prior art" for everything. The positive control caught it; without the control it was a clean, confident, wrong zero — the exact failure this whole item is about. git returns empty stdout when it genuinely fails, so nothing false is admitted by ignoring the code here.""" try: r = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=90) return r.stdout except Exception: return "" def owned_repos(): """Repos whose remotes point at the steward's hosts. Computed, never listed.""" out = sh(["find", str(HOME), "-maxdepth", "5", "-type", "d", "-name", ".git"]) repos = [] for line in out.splitlines(): if not line.strip(): continue r = Path(line).parent rem = sh(["git", "-C", str(r), "remote", "-v"]) if any(h in rem for h in OWNED_HOSTS): repos.append(r) return sorted(set(repos)) def search_commits(term, repos=None): """Every commit whose message mentions `term`, in any branch, with NO count window.""" hits = [] for r in (repos if repos is not None else owned_repos()): out = sh(["git", "-C", str(r), "log", "--all", "--format=%h\t%ad\t%s", "--date=short", "-i", f"--grep={term}"]) for line in out.splitlines(): parts = line.split("\t", 2) if len(parts) == 3: hits.append((r.name, parts[0], parts[1], parts[2])) return hits def register_mentions(term): """Items in the authorization record mentioning `term`. The other half of the asymmetry.""" n = 0 pat = re.compile(re.escape(term), re.I) for f in REGISTER: if f.exists(): n += len(pat.findall(f.read_text(errors="replace"))) return n def controls(): """Same-run positive controls. The jurist specified these: the instrument must return two commits already known to exist, or it measured nothing and its zero means nothing.""" ok, notes = True, [] hits = search_commits("LFS") shas = {h[1] for h in hits} for known, why in (("0677e8a", "chamber-library — LFS retired, 17 commits rewritten"), ("95760ff", "dotfiles — global git-lfs hooks removed")): if not any(s.startswith(known[:7]) for s in shas): ok = False notes.append(f"CONTROL FAIL: {known} not returned ({why})") # negative control: a term that cannot plausibly be in any commit message if search_commits("zzqqxx-not-a-real-term-9971"): ok = False notes.append("CONTROL FAIL: a nonsense term returned hits") return ok, notes, len(hits) def report(term): ok, notes, _ = controls() if not ok: print("⚠ INSTRUMENT NOT VERIFIED — the result below is unestablished.") for n in notes: print(" " + n) print() hits = search_commits(term) reg = register_mentions(term) print(f"PRIOR ART: {term!r}") print(f" commits mentioning it : {len(hits)} (all branches, no count window)") print(f" register mentions : {reg} (PENDING, PENDING-archive, REVIEWED)") print() for repo, sha, date, subj in sorted(hits, key=lambda h: h[2]): print(f" {date} {repo:34} {sha} {subj[:70]}") print() if hits and reg == 0: print(" ⚠ FINDING — PENDING-164's condition exactly: this mechanism has a history in") print(" the repos and NO trace in the authorization record. Whatever was decided") print(" about it was decided in a commit message. Read those commits before") print(" proposing anything about it.") elif hits: print(" Both surfaces carry it. Read the register items AND the commits — a ruling") print(" can post-date the commit that motivated it, or precede the one that undid it.") else: print(" No prior art found. ⚠ Absence here is weak evidence: it means no COMMIT") print(" MESSAGE names this term, not that nothing was decided about it.") return 0 if ok else 2 def census(): ok, notes, nlfs = controls() print("BACKWARD CENSUS — PENDING-164 AMENDMENT 1") print(f" instrument verified: {'YES' if ok else 'NO'}" + ("" if ok else " ⚠ RESULT UNESTABLISHED")) for n in notes: print(" " + n) repos = owned_repos() print(f" owned repos (computed from remotes): {len(repos)}") print(f" positive control: 'LFS' returns {nlfs} commits incl. 0677e8a and 95760ff") print() seen, rows = set(), [] for verb in CENSUS_VERBS: for repo, sha, date, subj in search_commits(verb, repos): if sha in seen: continue seen.add(sha) rows.append((date, repo, sha, subj)) print(f" candidate adoption/retirement commits: {len(rows)}") print(" (a candidate is a commit whose SUBJECT reads like a mechanism decision;") print(" classification is the reader's, not this instrument's)") print() for date, repo, sha, subj in sorted(rows, reverse=True): print(f" {date} {repo:34} {sha} {subj[:74]}") return 0 if ok else 2 if __name__ == "__main__": if len(sys.argv) > 1 and sys.argv[1] == "--census": sys.exit(census()) if len(sys.argv) > 1 and sys.argv[1] == "--selftest": ok, notes, n = controls() print(f"controls: {'PASS' if ok else 'FAIL'} ('LFS' → {n} commits)") for x in notes: print(" " + x) sys.exit(0 if ok else 1) if len(sys.argv) < 2: print("usage: prior-art.py | --census | --selftest") sys.exit(64) sys.exit(report(" ".join(sys.argv[1:])))