# MUTANT-GUARD REFACTOR VERIFICATION **Target:** `wizard-work/forge/verify_triage_dump_pending_refactor.py` **Instrument:** `wizard-work/forge/_mutant_scan_triage_dump_pending.py` **Lane:** Refactor (forge) **Verdict:** FIXED -- the `no_main_guard` mutant escape is closed. ## The defect (measured, not argued) The `no_main_guard` mutant strips the `if __name__ == "__main__":` guard from `_triage_dump_pending_refactored.py`, leaving `sys.exit(main())` at module scope. Before this fix, that mutant **SURVIVED** the verifier: * `check_path_override_honoured` called `importlib.import_module(MOD_NAME)` *unguarded* (no `try/except`). Importing the mutant module ran `main()`, which leaked the entire probe output to **real stdout** (the seed file's contents -- 7932 bytes, "SOURCE: 'Muse web artifact..." onward), then `sys.exit(0)` raised `SystemExit(0)`. * `SystemExit(0)` propagated out of `check_path_override_honoured`, out of the verifier's `main()`, and **terminated the verifier process with rc 0** -- before the verifier reached its final `sys.exit(1)`. * The verifier therefore reported green on a tree carrying a guard-less module. ## The fix (behavior-preserving on clean input) Extracted `_safe_import()` which centralises the safe-import pattern already used by `check_import_side_effect_free`: * pops `MOD_NAME` from `sys.modules` * ensures `WORK_DIR` is on `sys.path` * imports inside `redirect_stdout`/`redirect_stderr` (captures, never leaks) * catches every `BaseException` (including `SystemExit`) * returns `(module, exc, out, err)`; `exc` set iff module is `None` Both import sites now use it: * `check_import_side_effect_free` -- unchanged semantics: exc -> FAIL, output -> FAIL, else OK. * `check_path_override_honoured` -- was unguarded; now catches `exc` and FAILs instead of letting `SystemExit` kill the process. A defensive `assert mod is not None` makes the mutual-exclusion invariant explicit. `exc is None` implies `mod is not None`, so the subsequent `mod.main(path=custom)` call is unreachable when the import failed. ## Evidence (real commands, real outputs) ``` $ cd /root/troupe-standup/wizard-work/forge $ python3 verify_triage_dump_pending_refactor.py OK: py_compile passes OK: identical output (exit 0, 7932 bytes) OK: __main__ guard present OK: no 'global' statements in code OK: import is side-effect-free (no probe output at import) OK: _print_summary, _print_decided, _print_pending, load_data, main extracted; main calls all three print helpers OK: main() delegates via distinct standalone statements (no ';'-compaction) OK: main(path) accepts a configurable path parameter OK: original still exhibits the removed smells (refactor is non-vacuous) OK: main(path=custom) honours the override (prints custom file, not DEFAULT_PATH) === Verification: 10/10 checks passed === exit 0 ``` Mutation scan (copies artifacts to a temp dir, never touches the real files): ``` $ python3 _mutant_scan_triage_dump_pending.py no_main_guard DIED exit=1 === Verification: 7/10 checks passed === path_ignored DIED exit=1 === Verification: 9/10 checks passed === skip_pending DIED exit=1 === Verification: 8/10 checks passed === stub_data DIED exit=1 === Verification: 8/10 checks passed === remove_path_param DIED exit=1 TypeError: main() got an unexpected keyword argument 'path' hardcode_source DIED exit=1 === Verification: 9/10 checks passed === inline_helpers DIED exit=1 === Verification: 9/10 checks passed === Survivors: none exit 0 ``` Before the fix, `no_main_guard` was `SURVIVED` with a leaked probe dump on stdout and `exit=0`. After the fix it is `DIED` (exit 1, 7/10 checks): `check_import_side_effect_free` catches the `SystemExit` AND `check_path_override_honoured` now catches it too, so the verifier reaches its final verdict row and exits non-zero. ## Non-vacuity * The clean (unmutated) refactored module still passes 10/10 -- the added `_safe_import` guards report OK on a healthy import. * The mutant scan still kills every *other* mutant identically to before (the fix changes only the `no_main_guard` case). * Run twice for determinism; both runs report `Survivors: none`. ## Scope Only `verify_triage_dump_pending_refactor.py` changed. The subject module `_triage_dump_pending_refactored.py` is untouched (it already had the guard); the vulnerability was in the *verifier's* unguarded second import, not the refactored subject.