#!/usr/bin/env python3 """Option-surface ↔ README drift check (flake check `option-docs`). Usage: check-option-docs.py is one full option path per line (nomarchy.…), generated at eval time in flake.nix by walking the four option-declaring modules — so the "live surface" side can never go stale by itself. Two directions: 1. Every live option must be documented in the README: either its full path appears verbatim (a table row), or — for sub-knobs that don't end in `.enable` — a backticked `.leaf` shorthand appears somewhere (the convention for tuning knobs documented inline in their parent row, e.g. nightlight's `.temperature`). `.enable` options always get their own row: the shorthand `` `.enable` `` would match any of them. 2. Every `nomarchy.*` path in a README option-table row (lines starting `| `nomarchy.`) must still exist — catches rows for renamed/removed options. """ import re import sys surface_file, readme_file = sys.argv[1], sys.argv[2] surface = [l.strip() for l in open(surface_file) if l.strip()] readme = open(readme_file).read() # Backticked `.leaf` shorthands present in the README (inline sub-knob docs). shorthands = set(re.findall(r"`(\.[A-Za-z][A-Za-z0-9]*)`", readme)) failures = [] for opt in surface: if opt in readme: continue leaf = "." + opt.rsplit(".", 1)[1] if leaf != ".enable" and leaf in shorthands: continue failures.append( f"undocumented option: {opt} — add a README table row" + ("" if leaf == ".enable" else f" or an inline `{leaf}` mention") ) # Reverse: option paths claimed by table rows must exist. for line in readme.splitlines(): m = re.match(r"\|\s*`(nomarchy\.[A-Za-z0-9.]+)`", line) if m and m.group(1) not in surface: failures.append(f"stale README table row: {m.group(1)} is not a live option") if failures: print(f"option-docs: {len(failures)} problem(s)") for f in failures: print(" " + f) sys.exit(1) print(f"option-docs: {len(surface)} options all documented, no stale rows")