#!/usr/bin/env python3 """check_lint.py -- grade a Ralph-loop `CHECK:` line before it is run. WHY THIS EXISTS STANDING-AGREEMENT.md §9 and docs/ralph-loop.md define the delivery standard: every deliverable ends with one `CHECK: ` that exits 0 when the work is correct, and "a `CHECK:` that is a tautology is a fault, not a verification". The runner (/root/.hermes/scripts/ralph-loop.sh) extracts that line, runs it under `bash -c`, and prints GREEN or RED. Two things the runner does not do. Both were measured, not reasoned, on the loop's first fleet night -- 2026-10-16 16:57Z, eight wizards, logs under /root/.hermes/wizard-outputs/ralph: 1. IT DOES NOT READ THE LINE IT EXTRACTS. `... && echo PASS || echo FAIL` exits 0 whether the thing under test passed or failed. The runner has no defence against that: on that night two of the three GREENs were checks that could not fail. A CHECK that cannot fail is the fault §9 names. 2. IT DOES NOT STRIP WHAT FOLLOWS THE COMMAND. An em-dash explanation on the same line, or a carriage return the wizard's output carried, is handed to `bash -c` verbatim -- the runner's `sed` strips whitespace *after the colon*, never the `\\r` before the newline. Both are in that night's logs: one RED was a bash syntax error on prose, another was a file that did not exist under the name `...verify_chunker.py\\r`. This tool grades the line so a fault is visible before it costs an iteration. WHAT IT MEASURES -- SEVEN CLASSES, EACH WITH ITS RULE PRINTED MISSING no line matches `^\\s*CHECK:`; the runner calls this RED at once. EMPTY a CHECK line with nothing after the colon. TAUTOLOGY the whole command is `true`, `:`, `exit 0`, `echo `, `printf ` or `sleep `: nothing is read or tested. CATCH_ALL an unconditional zero exit anywhere in the chain -- a `||` arm of true, `:`, `exit 0`, `echo`, `printf`, or a terminal statement `; true` / `; :` / `; exit 0` / `; echo ...`. Every path returns 0, so the runner's GREEN carries no information. TRAILING_CR a literal carriage return survives at end of line; it reaches bash as part of the last word. PROSE_TAIL prose follows the command: an em dash, ` -- `, a trailing `(...)` aside, or a sentence break. CANDIDATE -- the boundary between a command and its gloss is not decidable by syntax, so the cut point is printed for a human to confirm. SOUND none of the above. NOT a promise. This tool never runs the command, so SOUND means only "no zero-exit construct and no trailing prose this rubric can name". Whether the command tests the right thing is not a syntactic question and is not graded. THE CROSS-CHECK, AND WHY IT IS THE POINT `--ralph-dir` reads the runner's own logs: it re-extracts each wizard's CHECK with the same algorithm the runner uses (last `^\\s*CHECK:` line wins, prefix stripped, nothing else removed) and joins it to the verdict the runner reported. A run the runner called GREEN whose line is TAUTOLOGY or CATCH_ALL is reported GREEN_BY_CONSTRUCTION: it passed because it could not fail. WHAT IT DOES NOT DO It never executes an extracted command. Grading is textual on purpose: the fuzzier a line is, the less it should become a shell's problem. Running the CHECK stays the runner's job. USAGE python3 check_lint.py --check "$CHECK" python3 check_lint.py --ralph-dir /root/.hermes/wizard-outputs/ralph python3 check_lint.py --selftest # 37 cells, one per class and edge """ from __future__ import annotations import argparse import datetime as _dt import json import os import re import sys from collections import Counter # -------------------------------------------------------------------------- # Extraction -- mirrors ralph-loop.sh line 24: # grep -iE "^\s*CHECK:" "$LOG" | tail -1 | sed -E 's/^\s*CHECK:[[:space:]]*//i' # The mirror is deliberate: the runner's own behaviour is the subject, so a # "cleaner" extraction here would stop reproducing what actually happened. # -------------------------------------------------------------------------- CHECK_LINE_RE = re.compile(r"^\s*CHECK:[ \t]*", re.IGNORECASE) def extract_check(text: str) -> str | None: """Last `^\\s*CHECK:` line, prefix stripped, trailing CR KEPT (the runner keeps it).""" found = None for line in text.split("\n"): m = CHECK_LINE_RE.match(line) if m: found = line[m.end():] return found # -------------------------------------------------------------------------- # The rubric # -------------------------------------------------------------------------- CONSTANT_RE = re.compile( r"^(?:true|:|exit\s+0|sleep\s+\d+(?:\.\d+)?" r"|echo(?:\s+[^;|&<>$`]*)?" r"|printf(?:\s+[^;|&<>$`]*)?)$" ) # `|| true` / `|| echo X` arms, and terminal `; true` / `; echo X` statements. # A `&& echo X` arm is deliberately NOT matched: it runs only if the left side # succeeded, so it preserves the failure signal. A `&&` is excluded from the # `[;&]` alternative by the lookbehinds, which stop the second `&`/`|` of a # two-character operator from being read as a single one. CATCH_ALL_RE = re.compile( r"(?:\|\|\s*(?:true|:|exit\s+0|echo\b|printf\b))" r"|(?:(? 0: if best is None or m.start() < best[2]: best = (name, core[: m.start()].rstrip(), m.start()) if best is None: return None return best[0], best[1] def classify(cmd: str | None): """-> (primary_class, faults, detail dict). Pure text; never executes.""" detail: dict = {} if cmd is None: return "MISSING", ["MISSING"], detail faults = [] if cmd.endswith("\r"): faults.append("TRAILING_CR") detail["cr_len"] = len(cmd) - len(cmd.rstrip("\r")) core = cmd.rstrip("\r") if core.strip() == "": faults.append("EMPTY") return "EMPTY", faults, detail m_catch = CATCH_ALL_RE.search(core) if CONSTANT_RE.match(core.strip()): faults.append("TAUTOLOGY") elif m_catch: faults.append("CATCH_ALL") detail["catch_all_match"] = m_catch.group(0) pt = prose_tail(core) if pt: faults.append("PROSE_TAIL") detail["prose_marker"], detail["command_prefix"] = pt primary = next((c for c in SEVERITY if c in faults), "SOUND") return primary, faults, detail def _write_json_file(path: str, obj) -> int: """Write obj as indented JSON to path. Returns 0 on success, 2 on OSError. Extracted from the two sites that previously hand-duplicated the open/dump/write/newline/except block: report() and the --check/--json branch in main(). Both used indent=2 with default sort_keys=False. """ try: with open(path, "w", encoding="utf-8") as fh: json.dump(obj, fh, indent=2, sort_keys=False) fh.write("\n") except OSError as exc: print("cannot write --json " + path + ": " + str(exc), file=sys.stderr) return 2 return 0 # -------------------------------------------------------------------------- # The runner's own logs # -------------------------------------------------------------------------- ITER_LOG_RE = re.compile(r"^(?P.+?)-(?P\d{4}-\d{2}-\d{2}-\d{6})-iter(?P\d+)\.txt$") def parse_runner_log(path: str) -> list[dict]: """Per-iteration verdicts from the runner's stdout log.""" with open(path, encoding="utf-8", errors="replace", newline="") as fh: text = fh.read() out = [] cur = None for raw in text.split("\n"): line = raw.rstrip("\r") m = re.match(r"=== RALPH .* iter (\d+)/", line) if m: cur = {"iter": int(m.group(1)), "verdict": None, "reported_check": None, "detail": ""} out.append(cur) continue if cur is None: continue if line.startswith(" running CHECK: "): cur["reported_check"] = line[len(" running CHECK: "):] elif "no CHECK line emitted" in line: cur["verdict"] = "NO_CHECK" elif "✅ GREEN after" in line: cur["verdict"] = "GREEN" elif "❌ RED" in line: cur["verdict"] = "RED" cur["detail"] = line.split("❌ RED —", 1)[-1].strip()[:200] return out def read_ralph_dir(path: str, include_unlogged: bool = False): """-> list of runs, newest stamp first. A run is one (stamp, wizard). The `.log` companion holds the runner's own verdicts, but the fleet driver names it `-.log` (minute resolution) while ralph-loop.sh names its iteration logs `--iterN.txt`. The join is therefore by prefix. Runs with no located runner log are reported only under --all: without a verdict there is nothing to cross-check, and the newest iter log may belong to a run that is still in flight. """ if not os.path.isdir(path): raise SystemExit(f"no such ralph dir: {path}") log_stamps: dict[str, list[str]] = {} for name in os.listdir(path): m = re.match(r"^(?P.+?)-(?P\d{4}-\d{2}-\d{2}-\d{4})\.log$", name) if m: log_stamps.setdefault(m.group("wizard"), []).append(m.group("lstr")) runs: dict[tuple, dict] = {} for name in sorted(os.listdir(path)): m = ITER_LOG_RE.match(name) if not m: continue key = (m.group("stamp"), m.group("wizard")) run = runs.setdefault(key, {"stamp": key[0], "wizard": key[1], "iters": {}}) with open(os.path.join(path, name), encoding="utf-8", errors="replace", newline="") as fh: run["iters"][int(m.group("n"))] = extract_check(fh.read()) selected = [] unlogged = 0 for (stamp, wizard), run in runs.items(): cand = [s for s in log_stamps.get(wizard, []) if stamp.startswith(s)] run["runner_log"] = (os.path.join(path, f"{wizard}-{max(cand)}.log") if cand else None) if run["runner_log"] is None: unlogged += 1 if not include_unlogged: continue selected.append(run) selected.sort(key=lambda r: (r["stamp"], r["wizard"]), reverse=True) return selected, unlogged def grade_run(run: dict) -> dict: iters = run["iters"] last_n = max(iters) if iters else 0 extracted = iters.get(last_n) primary, faults, detail = classify(extracted) verdicts = parse_runner_log(run["runner_log"]) if run["runner_log"] else [] by_iter = {v["iter"]: v for v in verdicts} reported = by_iter.get(last_n, {}).get("reported_check") final = "UNKNOWN" if verdicts: last = by_iter[max(by_iter)] final = last["verdict"] or "UNKNOWN" if final is None: final = "UNKNOWN" # the runner's RALPH RESULT line is authoritative for the run's outcome if run["runner_log"]: with open(run["runner_log"], encoding="utf-8", errors="replace", newline="") as fh: for line in fh.read().split("\n"): if "RALPH RESULT: GREEN" in line: final = "GREEN" elif "RALPH RESULT: EXHAUSTED" in line: final = "EXHAUSTED" cross = "none" if final == "GREEN" and primary in ("TAUTOLOGY", "CATCH_ALL"): cross = "GREEN_BY_CONSTRUCTION" elif final == "GREEN" and primary in ("PROSE_TAIL", "TRAILING_CR"): cross = "GREEN_DESPITE_FORMATTING" per_iteration = [] for n in sorted(iters): p_cls, p_faults, p_det = classify(iters[n]) v = by_iter.get(n, {}) per_iteration.append({ "iter": n, "class": p_cls, "faults": p_faults, "detail": p_det, "runner_verdict": v.get("verdict"), "runner_detail": v.get("detail", ""), "check": iters[n], }) return { "stamp": run["stamp"], "wizard": run["wizard"], "iterations": len(iters), "last_iter": last_n, "class": primary, "faults": faults, "detail": detail, "check": extracted, "per_iteration": per_iteration, "runner_final": final, "runner_reported_check": reported, "extraction_matches_runner": ( None if reported is None or extracted is None else reported.rstrip("\r") == extracted.rstrip("\r") ), "cross_check": cross, "runner_log": run["runner_log"], } # -------------------------------------------------------------------------- # Selftest # -------------------------------------------------------------------------- FIXTURES = [ # (what the wizard emitted, expected primary class) ("no check here at all\n", "MISSING"), ("CHECK:\n", "EMPTY"), ("CHECK: \n", "EMPTY"), ("CHECK: true\n", "TAUTOLOGY"), ("CHECK: :\n", "TAUTOLOGY"), ("CHECK: exit 0\n", "TAUTOLOGY"), ("CHECK: echo ok\n", "TAUTOLOGY"), ("CHECK: echo \"all good\"\n", "TAUTOLOGY"), ("CHECK: sleep 5\n", "TAUTOLOGY"), ("check: true\n", "TAUTOLOGY"), # runner's grep is -i ("CHECK: python3 x.py && echo OK\n", "SOUND"), ("CHECK: python3 x.py && echo PASS || echo FAIL\n", "CATCH_ALL"), ("CHECK: python3 x.py || true\n", "CATCH_ALL"), ("CHECK: python3 x.py || :\n", "CATCH_ALL"), ("CHECK: python3 x.py || exit 0\n", "CATCH_ALL"), ("CHECK: python3 x.py; echo done\n", "CATCH_ALL"), ("CHECK: python3 x.py; true\n", "CATCH_ALL"), ("CHECK: test -s f && grep -c '###' f | grep -qx '8' && echo PASS || echo FAIL\n", "CATCH_ALL"), ("CHECK: test \"$(python3 wc.py <<< \"a b c\")\" = \"3\" && echo PASSED || echo FAILED\n", "CATCH_ALL"), ("CHECK: python3 /tmp/verify_dl.py\r\n", "TRAILING_CR"), ("CHECK: cd /a && PYTHONPATH=/a python /a/verify_chunker.py\r\n", "TRAILING_CR"), ("CHECK: python3 /tmp/v.py \u2014 re-reads the JSON, asserts counts (Ran it: PASS, exit 0.)\n", "PROSE_TAIL"), ("CHECK: python3 v.py -- re-reads the JSON\n", "PROSE_TAIL"), ("CHECK: cd /x && python _verify.py && python test_a.py && python test_b.py\n", "SOUND"), ("CHECK: grep -q ralph-green /root/ralph-smoke.txt\n", "SOUND"), ("CHECK: sha256sum a.tar.gz | grep -q 4844c5fc\n", "SOUND"), ("CHECK: python3 tools/x.py --selftest | tail -1 | grep -q 'cells, 0 failed'\n", "SOUND"), ("CHECK: python3 -c \"print(1)\"\n", "SOUND"), # paren not preceded by space ("CHECK: python3 x.py --json /tmp/o.json\n", "SOUND"), # -- flag, not " -- " ("CHECK: python3 v.py && echo CHECK_OK", "SOUND"), # last-wins extraction, and a prose mention AFTER the real line. The second # fixture is sol's real shape (2026-10-16 16:57Z log, line 1335): a line that # begins with an indentation then CHECK: matches the runner's grep, so prose # written after the command becomes the graded line. Prose that does not # itself start with CHECK: (no leading-whitespace CHECK:) is not matched and # the earlier real line survives -- both directions are pinned here. ("CHECK: true\nCHECK: python3 x.py\n", "SOUND"), ("CHECK: python3 x.py\n\nA CHECK: line is required.\n", "SOUND"), ("CHECK: python3 x.py\n\n CHECK: line \u2014 one shell command that verifies " "your work and exits 0\n", "PROSE_TAIL"), ] EXTRACT_FIXTURES = [ (" CHECK: cmd here\n", "cmd here"), ("CHECK: cmd2\r\n", "cmd2\r"), ("no check\n", None), ("check: lower\n", "lower"), ] def selftest(verbose: bool) -> int: cells = 0 failed = 0 for text, want in FIXTURES: cells += 1 got, faults, _ = classify(extract_check(text)) if got != want: failed += 1 print(f" cell {cells}: want {want} got {got} <- {text!r}") elif verbose: print(f" cell {cells}: {got:11s} {text.strip()!r}") for text, want in EXTRACT_FIXTURES: cells += 1 got = extract_check(text) if got != want: failed += 1 print(f" cell {cells}: extract want {want!r} got {got!r} <- {text!r}") elif verbose: print(f" cell {cells}: extract {got!r}") print(f"selftest: {cells} cells, {cells - failed} passed, {failed} failed") return 1 if failed else 0 # -------------------------------------------------------------------------- # Report # -------------------------------------------------------------------------- def print_rules(): print("RULES (textual; the command is never executed by this tool)") print(" MISSING no line matches ^\\s*CHECK:") print(" EMPTY a CHECK line with nothing after the colon") print(" TAUTOLOGY whole command is true/: /exit 0/echo/printf/sleep") print(" CATCH_ALL a || arm or terminal ; statement that exits 0 regardless") print(" TRAILING_CR a literal carriage return survives at end of line") print(" PROSE_TAIL prose follows the command (CANDIDATE, cut point printed)") print(" SOUND none of the above -- not a promise about its meaning") def report(runs: list[dict], as_json: str | None, quiet: bool, unlogged: int = 0) -> int: graded = [grade_run(r) for r in runs] graded.sort(key=lambda r: (r["stamp"], r["wizard"]), reverse=True) summary = {"runs_on_wizards": len(graded), "green": 0, "exhausted": 0, "unknown": 0, "no_check_ever": 0, "classes": {}, "green_by_construction": 0, "green_despite_formatting": 0, "extraction_mismatches": 0, "runs_without_runner_log": unlogged} for g in graded: if g["runner_final"] == "GREEN": summary["green"] += 1 elif g["runner_final"] == "EXHAUSTED": summary["exhausted"] += 1 else: summary["unknown"] += 1 if g["class"] == "MISSING": summary["no_check_ever"] += 1 if g["cross_check"] == "GREEN_BY_CONSTRUCTION": summary["green_by_construction"] += 1 elif g["cross_check"] == "GREEN_DESPITE_FORMATTING": summary["green_despite_formatting"] += 1 if g["extraction_matches_runner"] is False: summary["extraction_mismatches"] += 1 # `collections.Counter` replaces the hand-rolled tally loop # (`summary["classes"][g["class"]] = summary["classes"].get(g["class"], 0) + 1`): # Counter reads a missing key as 0, so `dict(Counter(...))` yields the same # {class: n} mapping in first-appearance order. Re-assigning the existing # key keeps its position in `summary`, so the SUMMARY repr below and the # json.dump payload are both byte-identical to the pre-refactor output. summary["classes"] = dict(Counter(g["class"] for g in graded)) if not quiet: print_rules() stamp = None for g in graded: if g["stamp"] != stamp: stamp = g["stamp"] print(f"\nRUN {stamp}") print(f" {'wizard':8s} {'it':>3s} {'class':12s} {'runner':10s} " f"{'cross-check':24s} check") chk = g["check"] shown = "(none)" if chk is None else chk.rstrip("\r") shown = shown if len(shown) <= 68 else shown[:65] + "..." print(f" {g['wizard']:8s} {g['iterations']:3d} {g['class']:12s} " f"{g['runner_final']:10s} {g['cross_check']:24s} {shown}") if len(g["faults"]) > 1: print(f" also: {', '.join(f for f in g['faults'] if f != g['class'])}") if g["extraction_matches_runner"] is False: print(" NOTE: re-extraction differs from what the runner echoed") print("\nSUMMARY") for k, v in summary.items(): print(f" {k}: {v}") if as_json: payload = {"tool": "check_lint.py", "generated_utc": _dt.datetime.now(_dt.timezone.utc) .strftime("%Y-%m-%dT%H:%M:%SZ"), "ralph_dir": os.path.dirname(graded[0]["runner_log"]) if graded and graded[0]["runner_log"] else None, "runs": graded, "summary": summary} rc = _write_json_file(as_json, payload) if rc: return rc if not quiet: print(f"\njson -> {as_json}") return 0 def main(argv=None) -> int: ap = argparse.ArgumentParser( description="Grade Ralph-loop CHECK lines. Read-only: never executes a CHECK.") g = ap.add_mutually_exclusive_group() g.add_argument("--selftest", action="store_true", help="grade the built-in fixtures") g.add_argument("--ralph-dir", metavar="DIR", default=None, help="grade every run in a ralph-loop output directory") g.add_argument("--log", metavar="FILE", default=None, help="extract and grade the CHECK in one log file") ap.add_argument("--check", metavar="CMD", default=None, help="grade this command string directly") ap.add_argument("--json", metavar="FILE", default=None) ap.add_argument("--quiet", action="store_true") ap.add_argument("--verbose", action="store_true") ap.add_argument("--all", action="store_true", help="include runs that have no runner .log (no verdict to check)") ap.add_argument("--fail-on-fault", action="store_true", help="exit 1 if any graded CHECK is not SOUND") a = ap.parse_args(argv) if a.selftest: return selftest(a.verbose) if a.check is not None: primary, faults, detail = classify(a.check) if not a.quiet: print(f"{primary}: {a.check!r}") if faults: print(f" faults: {', '.join(faults)}") if detail: print(f" detail: {json.dumps(detail)}") if a.json: rc = _write_json_file(a.json, {"check": a.check, "class": primary, "faults": faults, "detail": detail}) if rc: return rc return 1 if (a.fail_on_fault and primary != "SOUND") else 0 if a.log: try: with open(a.log, encoding="utf-8", errors="replace", newline="") as fh: text = fh.read() except OSError as exc: # A log that is not there is a PRECONDITION failure, not a grading # result. It used to escape as a FileNotFoundError traceback with # rc 1, which is the code this tool uses for "the check has a # fault"; since 2026-10-17 the repo-wide contract is rc 2 for # "could not grade" (see tools/selftest_sweep.py's read-refusal # axis, which now measures this row on every reader). print(f"cannot read log: {exc}", file=sys.stderr) return 2 cmd = extract_check(text) primary, faults, detail = classify(cmd) if not a.quiet: print(f"{a.log}: {primary}") print(f" extracted: {cmd!r}") if faults: print(f" faults: {', '.join(faults)}") if detail: print(f" detail: {json.dumps(detail)}") return 1 if (a.fail_on_fault and primary != "SOUND") else 0 ralph_dir = a.ralph_dir or "/root/.hermes/wizard-outputs/ralph" runs, unlogged = read_ralph_dir(ralph_dir, include_unlogged=a.all) if not runs: print(f"no iter logs in {ralph_dir}") return 0 if not a.fail_on_fault else 1 rc = report(runs, a.json, a.quiet, unlogged) if a.fail_on_fault: graded = [grade_run(r) for r in runs] return 1 if any(g["class"] != "SOUND" for g in graded) else 0 return rc if __name__ == "__main__": sys.exit(main())