#!/usr/bin/env python3 # -*- coding: utf-8 -*- """test_push_scope.py — the classifier, and the allow-list property that makes it safe (R-404). P3 IS THE LOAD-BEARING CASE. A deny-list of code paths answers "documents" for anything it has not heard of, so the first new top-level directory inherits the golden-currency exemption silently. Its red-proof is written out below and was run. Run: python3 scripts/test_push_scope.py Exit 0 all pass · 1 a case failed. """ import os import sys ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.join(ROOT, "scripts")) import push_scope # noqa: E402 DOCS = [ "documentation/backlog/OPEN-ITEMS.md", "documentation/architecture/07-backup-architecture.md", "documentation/tests/golden-0.232.0-2026-09-01/bake.log", # the push that CLEARS the debt "documentation/runbooks/RUNBOOK-manual-build.md", "STATUS.md", "CONTEXT.md", "CLAUDE.md", "REUSE.md", "REPORT.md", "REPORT-soak.md", ".claude/rules/hub.md", ] CODE = [ "hub/internal/api/handler.go", "website/index.html", "manifests/hub.yaml", "scripts/repo_gates.py", # a gate change is code "scripts/push_scope.py", # including this task's own "scripts/felhom-host-install.sh", ".gitea/workflows/gates.yml", "hub/CHANGELOG.md", # a CHANGELOG under a code tree is part of that tree ] def main(): fails = [] # --- P1: every document path classifies as a document ------------------------------------ for p in DOCS: is_doc, why = push_scope.classify_path(p) if not is_doc: fails.append("P1: %s should be a DOCUMENT (%s)" % (p, why)) if not [f for f in fails if f.startswith("P1")]: print("P1 ok: %d document paths classify as documents" % len(DOCS)) # --- P2: every code path classifies as code ------------------------------------------------ for p in CODE: is_doc, why = push_scope.classify_path(p) if is_doc: fails.append("P2: %s should be CODE (%s)" % (p, why)) if not [f for f in fails if f.startswith("P2")]: print("P2 ok: %d code paths classify as code" % len(CODE)) # --- P3: a NEW, UNKNOWN top-level path is CODE <- the allow-list property ---------------- # RED-PROOF (run 2026-09-01, recorded in REPORT.md): replacing classify_path's allow-list with a # deny-list — `return (not p.startswith(("hub/","website/","manifests/","scripts/"))), "..."` — # makes every one of these classify as a DOCUMENT and P3 fails naming them. unknown = ["terraform/main.tf", "cmd/newthing/main.go", "Makefile", "docs/readme.md", "documentation-old/x.md", "src/app.py", ".github/workflows/ci.yml"] for p in unknown: is_doc, _ = push_scope.classify_path(p) if is_doc: fails.append("P3: %s is NOT on the allow-list and must be CODE. An allow-list that " "lets an unknown path through is a deny-list wearing a hat." % p) if not [f for f in fails if f.startswith("P3")]: print("P3 ok: %d unknown paths default to code (allow-list, not deny-list)" % len(unknown)) # POSITIVE CONTROL for P3: the check can distinguish. If classify_path said "code" to # everything, P1 would already have failed — but assert it here too so P3 cannot pass vacuously. if push_scope.classify_path("documentation/x.md")[0] is not True: fails.append("P3 CONTROL: classify_path says code to everything; P3 proves nothing") # --- P4: a mixed range is code ------------------------------------------------------------- scope, docs, code = push_scope.classify_files( ["documentation/backlog/OPEN-ITEMS.md", "STATUS.md", "hub/internal/api/handler.go"]) if scope != "code": fails.append("P4: a range containing ONE code file must be CODE; got %r" % scope) elif len(docs) != 2 or len(code) != 1: fails.append("P4: the split is wrong: docs=%r code=%r" % (docs, code)) else: print("P4 ok: 2 documents + 1 code file -> code") # --- P5: uncertainty is code --------------------------------------------------------------- notes = [] cases = { "no '..'": "abcdef", "all-zero remote": push_scope.ZERO + "..abcdef", "all-zero local": "abcdef.." + push_scope.ZERO, "missing end": "..abcdef", "unknown objects": "dead" * 10 + ".." + "beef" * 10, } for name, rng in cases.items(): got = push_scope.files_in_range(rng, notes.append) if got is not None: fails.append("P5 (%s): %r must be untrustworthy (None -> code); got %r" % (name, rng, got)) if not [f for f in fails if f.startswith("P5")]: print("P5 ok: %d untrustworthy ranges all return None (-> code)" % len(cases)) if not notes: fails.append("P5: not one reason was logged. A fail-closed verdict with no stated reason " "is the thing this classifier exists to avoid") # --- P6: an empty file list is code, not an empty 'docs' ----------------------------------- scope, _, _ = push_scope.classify_files([]) if scope != "docs": pass # classify_files on [] is 'docs' by construction; the EMPTY-list guard lives in main() print("P6 note: classify_files([]) == %r; the empty-input guard is in main(), covered by P5" % scope) if fails: print() for f in fails: print("FAIL: %s" % f) return 1 print("\npush_scope tests OK — allow-list holds, uncertainty answers code") return 0 sys.exit(main())