import hashlib
import json
import re
import subprocess
import sys
FILE_MAX, TOTAL_MAX, ITEM_MIN, ANCHOR_MIN = 128 * 1024, 512 * 1024, 180, 0.33
TRUNC_BUDGETS = (200, 300, 400, 500, 600, 800, 1000, 1200, 1400, 1500, 2000, 4000)
ROLES = {"claims", "lanes", "judges", "agy", "pmat", "proposals", "crux"}
SEC_RE = re.compile(r"(?m)^#{2,4}\s+(CONFIRMED|REFUTED)\b")
ITEM_RE = re.compile(r"(?m)^(?=\d+\.\s+\[)")
STOP_RE = re.compile(r"(?m)^(?:\d+\.\s+\[|#)")
SUB_RE = re.compile(r"(?m)^\s*-\s+(evidence|corrected):\s*(.*)$")
CIT_RE = re.compile(
r"\b((?:src|tests|scripts|benches)/[A-Za-z0-9_./-]+\.rs"
r"|(?<![\w./-])(?:Cargo\.toml|Cargo\.lock|CHANGELOG\.md|README\.md)):(\d+)\b"
)
CIT_RE_TRIAGE = re.compile(
r"\b((?:src|tests|scripts|benches)/[A-Za-z0-9_./-]+\.rs"
r"|docs/[A-Za-z0-9_./-]+\.(?:md|yaml|jsonl)"
r"|(?<![\w./-])(?:Cargo\.toml|Cargo\.lock|CHANGELOG\.md|README\.md)):(\d+)\b"
)
def citation_shape(receipt_kind):
return CIT_RE_TRIAGE if receipt_kind == "triage" else CIT_RE
LEAKS = [
("user_at_ip", re.compile(r"\b[A-Za-z0-9._-]+@(?:\d{1,3}\.){3}\d{1,3}\b")),
("sshd_user", re.compile(r"sshd:\s*(?!<)[A-Za-z0-9._-]+@")),
("ssh_controlpath", re.compile(r"ControlPath=(?!<)\S+")),
("home_path", re.compile(r"/(?:home|Users)/(?!<)[A-Za-z0-9._-]+")),
("mac_scratch", re.compile(r"/var/folders/[A-Za-z0-9+_/-]{6,}")),
("claude_scratch", re.compile(r"/tmp/claude-\d+")),
("session_uuid", re.compile(r"\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b")),
("aws_access_key", re.compile(r"AKIA[0-9A-Z]{16}")),
("github_token", re.compile(r"gh[pso]_[A-Za-z0-9_]{36,}")),
("private_key", re.compile(r"-----BEGIN (?:RSA|EC|DSA|OPENSSH) PRIVATE KEY-----")),
("stripe_key", re.compile(r"[sr]k_(?:live|test)_[A-Za-z0-9]{20,}")),
("age_secret", re.compile(r"AGE-SECRET-KEY-1[A-Z0-9]{58}")),
("jwt", re.compile(r"eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}")),
("db_url_creds", re.compile(r"(?i)(?:mysql|postgres|postgresql|mongodb)://[^\s:/]+:[^\s@]+@")),
("slack_webhook", re.compile(r"https://hooks\.slack\.com/services/\S+")),
]
def die(msg):
print(f"\u2717 QUORUM GATE: {msg}", file=sys.stderr)
sys.exit(1)
def git(*args, binary=False):
p = subprocess.run(["git", *args], capture_output=True)
return p.returncode, (p.stdout if binary else p.stdout.decode("utf-8", "replace"))
def blob_at(rev, path):
rc, out = git("cat-file", "blob", f"{rev}:{path}", binary=True)
return out if rc == 0 else None
def sections(text):
heads = list(SEC_RE.finditer(text))
for i, h in enumerate(heads):
end = heads[i + 1].start() if i + 1 < len(heads) else len(text)
yield h.group(1), text[h.end():end]
def items(body):
out = []
for part in ITEM_RE.split(body):
if not re.match(r"\d+\.\s+\[", part):
continue
m = STOP_RE.search(part, 1)
out.append(part[:m.start()] if m else part)
return out
def truncated(body):
n = len(body)
if n in TRUNC_BUDGETS:
return f"length is exactly {n} -- a writer budget, not a sentence"
if body.count("`") % 2 == 1:
return "ends inside an unclosed backtick span"
return None
def check_entry_shape(e, listed):
path, rs = e.get("path", ""), e.get("roles", [])
if not path.startswith(".quorum/evidence/"):
die(f"evidence path '{path}' is outside .quorum/evidence/")
if path in listed:
die(f"evidence path '{path}' is listed twice")
if not isinstance(rs, list) or not rs or set(rs) - ROLES:
die(f"evidence '{path}' roles {rs!r}; must be a non-empty subset of {sorted(ROLES)}")
return path, rs
def committed_blob(e, head, path):
raw = blob_at(head, path)
if raw is None:
die(f"evidence '{path}' is NOT COMMITTED at HEAD.\n"
f" The gate reads the tree; CI reviews what was PUSHED.\n"
f" Commit it: git add {path} && git commit")
bid = git("rev-parse", f"{head}:{path}")[1].strip()
sha = hashlib.sha256(raw).hexdigest()
for field, got, want in (("blob", e.get("blob"), bid),
("sha256", e.get("sha256"), sha),
("bytes", e.get("bytes"), len(raw))):
if got != want:
die(f"evidence '{path}': receipt {field}={str(got)[:16]}, committed {str(want)[:16]}")
if len(raw) > FILE_MAX:
die(f"evidence '{path}' is {len(raw)}B over the {FILE_MAX}B ceiling. Split by role;\n"
" the raw journal belongs in an expiring artifact, not in git.")
return bid, raw
def check_totals_and_roles(ev, total, roles):
if total != ev.get("total_bytes"):
die(f"evidence.total_bytes={ev.get('total_bytes')}, committed blobs sum to {total}")
if total > TOTAL_MAX:
die(f"evidence totals {total}B over the {TOTAL_MAX}B ceiling")
for need in ("claims", "lanes", "judges", "agy"):
if need not in roles:
die(f"no evidence file carries role '{need}'. The rule names lane summaries,\n"
" judge scores and the independent review as evidence -- not only claims.")
def check_manifest(ev, head, blobs):
listed, roles, total = set(), set(), 0
for e in ev["files"]:
path, rs = check_entry_shape(e, listed)
listed.add(path)
roles.update(rs)
bid, raw = committed_blob(e, head, path)
blobs[path] = (bid, raw)
total += len(raw)
check_totals_and_roles(ev, total, roles)
return listed
def check_provenance(listed, blobs, touched):
if not (listed & touched):
die("no evidence file is touched by this branch. Every listed file already\n"
" existed unchanged -- which is exactly what a recycled receipt looks like.")
rc, out = git("ls-tree", "-r", "origin/main", "--", ".quorum/evidence")
prior = {ln.split()[2] for ln in out.splitlines() if len(ln.split()) > 2}
for path, (bid, _) in blobs.items():
if bid in prior:
die(f"evidence '{path}' is byte-identical to a blob already on origin/main.\n"
" Copying a merged PR's evidence forward is the cheapest forgery there is.")
def check_redaction(blobs):
for path, (_, raw) in blobs.items():
text = raw.decode("utf-8", "replace")
for name, rx in LEAKS:
m = rx.search(text)
if m:
line = text[:m.start()].count("\n") + 1
die(f"'{path}':{line} leaks {name}: {m.group(0)[:48]!r}\n"
" Scrub it (<user>, <host>, <SCRATCH>, <REPO>) and re-commit.\n"
" THIS REPO IS PUBLIC -- this exact class already leaked once.")
def check_item(it, kind, dp, seen):
head_line = re.sub(r"\s+", " ", it.split("\n")[0]).strip().lower()
if head_line in seen:
die(f"'{dp}': duplicate claim {head_line[:60]!r} -- "
"N copies of one sentence is a tally with extra steps")
seen.add(head_line)
if len(it) < ITEM_MIN:
die(f"'{dp}': a {kind} claim is {len(it)}B, floor {ITEM_MIN}B")
subs = SUB_RE.findall(it)
if not subs:
die(f"'{dp}': a {kind} claim carries no '- evidence:'/'- corrected:' subline")
for _, sb in subs:
check_subline(sb, kind, dp)
def check_subline(sb, kind, dp):
why = truncated(sb.rstrip())
if why:
die(f"'{dp}': a {kind} subline is TRUNCATED -- {why}.\n"
f" ...{sb.rstrip()[-60:]!r}\n"
" Fix the EMITTER: a severed citation is unreviewable and no\n"
" other tier survives to complete it.")
def check_tallies(counts, want_conf, want_ref):
for kind, want, label in (("CONFIRMED", want_conf, "claims_confirmed"),
("REFUTED", want_ref, "claims_refuted")):
if counts[kind] != want:
die(f"{label}={want} but the digest carries {counts[kind]} {kind} claims.\n"
" A tally that disagrees with its own prose is the black box this gate\n"
" already rejects for the refuted side.")
def check_claims(blobs, dp, want_conf, want_ref, base, head, touched, cit_re):
text = blobs[dp][1].decode("utf-8", "replace")
counts, seen, adjudicated = {"CONFIRMED": 0, "REFUTED": 0}, set(), []
for kind, body in sections(text):
for it in items(body):
counts[kind] += 1
adjudicated.append(it)
check_item(it, kind, dp, seen)
check_tallies(counts, want_conf, want_ref)
check_anchors(adjudicated, dp, base, head, touched, cit_re)
def anchors_at_base(src, dp, p, n, touched):
if int(n) > src.count(b"\n") + 1:
die(f"'{dp}': cites '{p}:{n}' but that file has {src.count(chr(10).encode())+1} lines at base")
return p in touched
def anchors_as_added(head, dp, p, n, touched):
if p not in touched:
return False
added = blob_at(head, p)
if added is None:
return False
if int(n) > added.count(b"\n") + 1:
die(f"'{dp}': cites '{p}:{n}' but that file, which this branch "
f"ADDS, has {added.count(chr(10).encode())+1} lines at HEAD")
return True
def check_anchors(adjudicated, dp, base, head, touched, cit_re):
anchored = 0
for it in adjudicated:
hit = False
for p, n in cit_re.findall(it):
src = blob_at(base, p)
if src is None:
hit = anchors_as_added(head, dp, p, n, touched) or hit
else:
hit = anchors_at_base(src, dp, p, n, touched) or hit
anchored += 1 if hit else 0
if adjudicated:
rate = anchored / len(adjudicated)
if rate < ANCHOR_MIN:
die(f"only {anchored}/{len(adjudicated)} ({rate:.0%}) adjudicated claims cite a\n"
f" file:line inside this branch's own diff; floor is {ANCHOR_MIN:.0%}.\n"
" Prose about code the branch never touched is not evidence FOR this change.")
def receipt_identity(r, base):
receipt_kind = r.get("kind", "code")
if receipt_kind not in ("code", "triage"):
die(f"receipt kind={receipt_kind!r} is not one of code | triage")
got = r.get("base_commit")
if not got:
die(f"receipt is missing 'base_commit'. Without it every file:line citation\n"
f" rots the moment this branch's own fix moves a line. Set it to {base}.")
if got != base:
die(f"receipt base_commit={got[:12]}... but the merge-base is {base[:12]}...")
ev = r.get("evidence")
if not isinstance(ev, dict) or not ev.get("files"):
die("receipt has no evidence.files[]. A verdict with no attached reasoning is\n"
" the bare integer the owner's rule rejects.")
return receipt_kind, ev
def main():
receipt, want_conf, want_ref, touched_raw, base, head = sys.argv[1:7]
want_conf, want_ref = int(want_conf), int(want_ref)
touched = set(filter(None, touched_raw.splitlines()))
try:
r = json.load(open(receipt))
except Exception as e:
die(f"{receipt} is not valid JSON: {e}")
receipt_kind, ev = receipt_identity(r, base)
blobs = {}
listed = check_manifest(ev, head, blobs)
check_provenance(listed, blobs, touched)
check_redaction(blobs)
dp = ev.get("claims_digest")
if dp not in blobs:
die("evidence.claims_digest must name one of evidence.files[]")
check_claims(blobs, dp, want_conf, want_ref, base, head, touched,
citation_shape(receipt_kind))
kind_note = " (kind: triage -- documentation the branch touches anchors)" \
if receipt_kind == "triage" else ""
print(f" evidence: {len(blobs)} files, {ev['total_bytes']}B, "
f"{want_conf} confirmed + {want_ref} refuted, redaction clean{kind_note}")
if __name__ == "__main__":
main()