scripts/verify.py
scripts/verify.pyBrowse 4 files
1,293 tokens
5,474 bytes
Token encoding: o200k_base
Snapshot 39882c6
← Back to SKILL.md
1#!/usr/bin/env python32"""Verify a soft-wrap pass changed nothing but whitespace.3 4For each target file, compares the current (transformed) content against a5reference -- either a git ref (the pre-transform state) or a reference6directory -- on three deterministic axes:7 8 1. Content invariant non-whitespace bytes identical (catches any lost,9 added, or reordered content).10 2. Render equality CommonMark + GFM-table rendered HTML identical after11 whitespace normalization (catches structural mis-joins:12 merged paragraphs, list items, headings, lost hard13 breaks, collapsed pipe tables). Requires markdown-it-py.14 3. Idempotency re-running the reflow is a no-op (the file is at a15 stable fixed point and won't churn under later edits).16 17A check that can't run is not a check that passed. A file with no reference18(new or untracked) or a render check with no markdown-it-py reports NOT VERIFIED19and exits non-zero, the same way a failure does -- a gate that reports success20for work it never inspected is worse than no gate.21 22Exit code is non-zero if any file fails a check or if any check couldn't run.23 24Usage:25 verify.py PATH [PATH ...] # compare working tree vs HEAD26 verify.py --ref ORIG_SHA PATH ... # compare vs an explicit git ref27 verify.py --against-dir DIR PATH ... # compare vs DIR (match basename)28 29A dir PATH is searched recursively for *.md / *.markdown.30"""31import argparse32import os33import re34import subprocess35import sys36 37sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))38from softwrap import reflow, collect # noqa: E40239 40try:41 from markdown_it import MarkdownIt42 43 # CommonMark plus the GFM table rule. Plain CommonMark has no table44 # extension, so it renders a pipe table and a one-line collapse of that45 # table to identical text and is blind to a destroyed table. Enabling46 # "table" lets render-equality catch a collapsed table (a <table> on one47 # side becomes a <p> on the other).48 _MD = MarkdownIt("commonmark").enable("table")49except Exception:50 _MD = None51 52 53def norm(html):54 return re.sub(r"\s+", " ", html).strip()55 56 57def render_equal(ref_text, work_text):58 if _MD is None:59 return None # check unavailable60 return norm(_MD.render(ref_text)) == norm(_MD.render(work_text))61 62 63def nows(s):64 return re.sub(r"\s+", "", s)65 66 67def get_reference(path, ref, against_dir):68 if against_dir:69 cand = os.path.join(against_dir, os.path.basename(path))70 if not os.path.isfile(cand):71 return None, f"no reference file {cand}"72 return open(cand, encoding="utf-8").read(), None73 d = os.path.dirname(os.path.abspath(path))74 base = os.path.basename(path)75 try:76 out = subprocess.run(77 ["git", "-C", d, "show", f"{ref}:./{base}"],78 capture_output=True,79 text=True,80 check=True,81 )82 return out.stdout, None83 except subprocess.CalledProcessError:84 return None, f"not in git ref {ref} (new file?)"85 86 87def main():88 ap = argparse.ArgumentParser(89 description="Verify a soft-wrap pass is whitespace-only."90 )91 ap.add_argument("paths", nargs="+")92 ap.add_argument(93 "--ref",94 default="HEAD",95 help="git ref for the pre-transform state (default HEAD)",96 )97 ap.add_argument(98 "--against-dir",99 help="compare against reference files in this dir (by basename)",100 )101 args = ap.parse_args()102 103 if _MD is None:104 print(105 "ERROR: markdown-it-py not installed; the render-equality check can't\n"106 " run, so no file can be reported as verified.\n"107 " Install with: pip install markdown-it-py\n",108 file=sys.stderr,109 )110 111 files = collect(args.paths)112 if not files:113 print("no markdown files matched", file=sys.stderr)114 return 2115 116 fails = 0117 unverified = 0118 for path in files:119 work = open(path, encoding="utf-8").read()120 ref_text, err = get_reference(path, args.ref, args.against_dir)121 122 idem_ok = reflow(work)[0] == work123 if ref_text is None:124 content_ok = None125 render_ok = None126 else:127 content_ok = nows(ref_text) == nows(work)128 render_ok = render_equal(ref_text, work)129 130 def mark(v):131 return {True: "ok", False: "FAIL", None: "n/v"}[v]132 133 if content_ok is False or render_ok is False or not idem_ok:134 fails += 1135 tag = " <-- FAIL"136 elif content_ok is None or render_ok is None:137 unverified += 1138 reason = err or "markdown-it-py not installed"139 tag = f" <-- NOT VERIFIED ({reason})"140 else:141 tag = ""142 print(143 f" content={mark(content_ok):<4} render={mark(render_ok):<4} "144 f"idempotent={mark(idem_ok):<4} {path}{tag}"145 )146 147 print()148 if fails or unverified:149 parts = []150 if fails:151 parts.append(f"{fails} file(s) FAILED verification")152 if unverified:153 parts.append(f"{unverified} file(s) NOT VERIFIED")154 print("RESULT: " + ", ".join(parts) + ".")155 return 1156 print(f"RESULT: all {len(files)} file(s) verified.")157 return 0158 159 160if __name__ == "__main__":161 sys.exit(main())162