#!/usr/bin/env python3 """Safe mutation-test for the forge probe-refactor verifier. Copies the verifier + the probe files + chunker into a TEMP dir (the real artifact is never modified), applies each mutant to the temp copy of ``_probe_forge_refactored.py``, runs the temp copy of the verifier, and reports SURVIVED / DIED. Exit 0 = every mutant is killed (the verifier is robust). Exit 1 = at least one mutant survived (verifier too weak). """ from __future__ import annotations import shutil import subprocess import sys import tempfile from pathlib import Path WORK = Path("/root/troupe-standup/wizard-work/forge") SRC_FILES = [ "verify_probe_forge_refactor.py", "_probe_forge.py", "_probe_forge_refactored.py", "chunker.py", ] mutants = [] def mutant(label, fn): mutants.append((label, fn)) mutant("guardless", lambda s: s.replace( 'if __name__ == "__main__":\n sys.exit(main())', 'main() # guard-less mutant\n')) mutant("no_main_guard", lambda s: s.replace( 'if __name__ == "__main__":\n sys.exit(main())', 'sys.exit(main()) # no_main_guard mutant\n')) mutant("ffd_flip", lambda s: s.replace( "if actual[i] != expected[i]:", "if actual[i] == expected[i]:")) mutant("ffd_return_none", lambda s: s.replace( " return i\n", " return None # mutant\n")) mutant("ffd_return_i_plus_1", lambda s: s.replace( " return i\n", " return i + 1 # mutant\n")) mutant("ffd_delete_return", lambda s: s.replace( " return i\n", " pass # mutant\n")) mutant("ffd_skip_index_zero", lambda s: s.replace( "for i in range(min(len(actual), len(expected))):", "for i in range(1, min(len(actual), len(expected))):")) mutant("ffd_extend_range", lambda s: s.replace( "for i in range(min(len(actual), len(expected))):", "for i in range(min(len(actual), len(expected)) + 1):")) mutant("ffd_always_return_none", lambda s: s.replace( " for i in range(min(len(actual), len(expected))):\n", " return None # mutant\n for i in range(min(len(actual), len(expected))):\n")) mutant("hardcode_equal", lambda s: s.replace( 'print("equal:", actual == expected)', 'print("equal:", True)')) mutant("reconstruct_overlap_zero", lambda s: s.replace( "actual = chunker.reconstruct(cs, 8)", "actual = chunker.reconstruct(cs, 0)")) def main() -> int: tmp = Path(tempfile.mkdtemp(prefix="mutant_scan_")) try: for f in SRC_FILES: shutil.copy2(WORK / f, tmp / f) ref = tmp / "_probe_forge_refactored.py" SRC = ref.read_text() survivors = [] for label, fn in mutants: mutant_src = fn(SRC) if mutant_src == SRC: print(f" {label:26s} SKIP (no change)") continue ref.write_text(mutant_src) p = subprocess.run("python3 verify_probe_forge_refactor.py", shell=True, cwd=tmp, capture_output=True, text=True) tail = (p.stdout + p.stderr).strip().splitlines() tail = tail[-1] if tail else "(no output)" died = p.returncode != 0 print(f" {label:26s} {'DIED' if died else 'SURVIVED':9s} " f"exit={p.returncode} {tail}") if not died: survivors.append(label) print("\nSurvivors:", survivors if survivors else "none") return 1 if survivors else 0 finally: shutil.rmtree(tmp, ignore_errors=True) if __name__ == "__main__": sys.exit(main())