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:
- Import-time side effect. The
for f in sorted(os.listdir(TOOLS))loop and everyprint(...)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. - Redundant
orclause."len([" in l or "len([" in l-- the second operand is identical to the first, sooradds no match; dead logic that only reads as "len([...]) vs len([...]) patterns" confusion. - Unused
sysimport.import ast, os, hashlib, collections, sys--sysis never referenced anywhere in the body. - Resource leak.
src = open(path, encoding="utf-8").read()-- file handle never closed (nowithblock). - No
__main__guard. The scan runs on import.
The refactor
_scan_dry_refactored.py:
- Moved the scan loop into
main()and guarded it withif __name__ == "__main__": main()-- import is now side-effect-free. - Collapsed
"len([" in l or "len([" in lto"len([" in l-- identical count, zero behavior change. - Dropped the unused
sysimport. - Replaced the bare
open()with awith 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 constantsREPEATED_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.