test-preserving-refactor · diff
git:20260502.ff9d243 to git:20260606.d9d0aab
54 added, 10 removed. Audit A to A.
---
name: test-preserving-refactor
- description: "Use to restructure code while guaranteeing that all existing tests continue to pass."
+ description: Use when code needs restructuring and observable behavior must stay unchanged, with existing tests kept green at every step.
---
- # Test-Preserving Refactor
+ ## Purpose
- Refactoring is only safe if it is backed by tests.
+ Refactoring is only safe if it is backed by tests. Work in a strict loop — green baseline, one structural change, immediate re-run, revert on red — so you are never trapped in an uncompilable state.
- ## The Protocol
+ ## When to Use
- 1. **Run Tests First**: Before touching any code, run the tests covering the target area. They MUST be green. If they are red, stop and fix the tests (or the code) first.
- 2. **Small Steps**: Make one structural change at a time (e.g., extract a method).
- 3. **Run Tests Immediately**: Run the tests immediately after the single structural change.
- 4. **Revert on Red**: If the tests fail, you made a mistake. Revert the change (`git checkout` or `ctrl+z`) and try a different approach. Do not attempt to "fix" the refactor while tests are failing.
- 5. **Commit**: Once the small change is green, consider it a safe checkpoint.
+ - Code needs restructuring (extract/inline/move) without behavior change
+ - A previous refactor spiraled into a broken, hard-to-recover state
+ - Tests covering the target area exist or can be run
- This strict Red/Green/Refactor cycle prevents you from getting trapped in an uncompilable state.
+ ## Inputs
+
+ - The target area and the command that runs just its tests
+
+ ## Workflow
+
+ 1. **Run tests first.** Before touching any code, run the tests covering the target area. They MUST be green. If they are red, stop and fix the tests (or the code) first:
+
+ ```bash
+ npx jest path/to/target # or: pytest tests/test_target.py -q
+ ```
+
+ 2. **Small steps.** Make one structural change at a time (e.g., extract a method).
+
+ 3. **Run tests immediately** after the single structural change. The per-change loop:
+
+ ```bash
+ # repeat per structural change: edit -> test -> commit (green) | revert (red)
+ npx jest path/to/target && git commit -am "refactor: extract X" || git checkout -- .
+ ```
+
+ 4. **Revert on red.** If the tests fail, you made a mistake. Revert the change (`git checkout -- .` or `git restore .`) and try a different approach. Do not attempt to "fix" the refactor while tests are failing.
+
+ 5. **Commit on green.** Once the small change is green, consider it a safe checkpoint.
+
+ This strict loop prevents you from getting trapped in an uncompilable state.
+
+ ## Output
+
+ - A chain of small, individually green commits
+ - The final test run output proving unchanged behavior
+
+ ## Verification
+
+ - [ ] Baseline test run was green before the first edit
+ - [ ] Exactly one structural change per test cycle
+ - [ ] Every red result handled by revert, not forward-fixing
+ - [ ] Each green step committed as a checkpoint
+ - [ ] No test was modified to make the refactor pass
+
+ ## Failure Modes
+
+ - **Refactoring on red** — starting from failing tests means you cannot tell what you broke.
+ - **Step batching** — three changes per test run; when it fails, you don't know which one did it.
+ - **Forward-fixing a broken refactor** — "fixing" while red digs the hole deeper; revert is cheaper.
+ - **Bending the tests** — editing assertions to match new behavior is a behavior change, not a refactor.
+