Files
troupe-standup/wizard-work/forge/SCAN_DRY_REFACTOR_VERIFICATION.md
T

5.2 KiB

_scan_dry refactor verification (forge lane: Refactor)

The shipped artifact

Three files under wizard-work/forge/:

  • _scan_dry.py -- original probe: a DRY smell-scanner that itself runs its full scan (and prints a 4811-byte report) at module top level.
  • _scan_dry_refactored.py -- refactored probe with smells removed.
  • verify_scan_dry_refactor.py -- equivalence + smell-absence + non-vacuity harness (11 checks).

Evidence files: _scan_dry_out.txt (original stdout+stderr, cksum 2478253836 4811), _scan_dry_verifier_out.txt (full verifier run).

The smell (what the refactor fixes)

The probe is a DRY scanner, so this is dogfooding -- the fixes are the smells a detector should flag on its own author:

  1. Import-time side effect. The for f in sorted(os.listdir(TOOLS)) loop and every print(...) run at module top level. Importing the module (for a test, harness, or monkeypatch) executes the whole scan and emits ~4.8 KB of output -- a side effect masquerading as init.
  2. Redundant or clause. "len([" in l or "len([" in l -- the second operand is identical to the first, so or adds no match; dead logic that only reads as "len([...]) vs len([...]) patterns" confusion.
  3. Unused sys import. import ast, os, hashlib, collections, sys -- sys is never referenced anywhere in the body.
  4. Resource leak. src = open(path, encoding="utf-8").read() -- file handle never closed (no with block).
  5. No __main__ guard. The scan runs on import.

The refactor

_scan_dry_refactored.py:

  • Moved the scan loop into main() and guarded it with if __name__ == "__main__": main() -- import is now side-effect-free.
  • Collapsed "len([" in l or "len([" in l to "len([" in l -- identical count, zero behavior change.
  • Dropped the unused sys import.
  • Replaced the bare open() with a with open(...) as fh: context manager.
  • Extracted two pure, testable helpers:
    • scan_file(path) -- returns (info, dup_funcs, repeated_lines, nlines, funcs) (Counter of hits + duplicate-function detection + repeated-line report).
    • format_report(...) -- returns the per-file report as a list of strings (pure: no I/O), so the formatting is unit-testable without running the probe.
  • Replaced magic literals (3, 15, 12) with named constants REPEATED_MIN, REPEATED_MIN_LEN, BODY_HASH_LEN.

Output is byte-identical to the original: 4811 bytes, cksum 2478253836.

Verifier bug found and fixed in this pass

The first non-vacuity guard (check_original_still_smells) had inverted logic on the __main__ leg: it appended a "problem" when the original lacked a main guard -- but lacking the guard is the smell we fixed, not a problem. Corrected so the guard passes iff the original still exhibits the removed smells (no guard, doubled or, bare open, module-level scan loop). Without this, a vacuous "refactor" that deleted nothing would pass -- the guard now fails closed: the original must still smell.

Evidence (measured, not claimed)

Byte-identical stdout+stderr (original vs refactored), captured by the verifier's check_identical_output:

$ cksum _scan_dry_out.txt
2478253836 4811 _scan_dry_out.txt
OK: identical output (exit 0, 4811 bytes)

Verifier run (11/11):

$ python3 verify_scan_dry_refactor.py; echo "VERIFIER_EXIT=$?"
OK: py_compile passes
OK: identical output (exit 0, 4811 bytes)
OK: __main__ guard present
OK: no 'global' statements in code
OK: unused 'sys' import removed
OK: redundant doubled 'or' clause removed; single 'len([ in l' present
OK: bare open() replaced with 'with' context manager
OK: scan_file, format_report, main extracted; main() calls scan_file
OK: import is side-effect-free (no probe output at import)
OK: scan_file real (/root/troupe-standup/tools/check_lint.py: 574 lines, 11 funcs); dup-body detection works
OK: original still exhibits the removed smells (refactor is non-vacuous)

=== Verification: 11/11 checks passed ===
VERIFIER_EXIT=0

Metrics (smell counts, before -> after)

smell original refactored
import-time side effects (module-level scan+print) yes none (guarded main())
__main__ guard absent present
redundant or clause ("len([" in l or "len([" in l) present removed (single "len([" in l)
unused sys import present removed
resource-leaking bare open() present with context manager
module-level mutable control flow yes (for+if+print at top level) none (pure scan_file/format_report + main)
byte-identical output (cksum) 2478253836 4811 2478253836 4811

Verdict

Behavior-preserving and measurable: py_compile clean, byte-identical output (4811 bytes, identical cksum), all 11 verifier checks pass (exit 0) including the non-vacuity guard that the original still smelled. The scanner's own import-time-side-effect, redundant-or, dead-import, and resource-leak smells are eliminated, and the scan logic is decoupled into pure helpers.