---
name: code-cleanup
description: Locks working behavior, then simplifies the changed code and re-verifies without expanding scope. Use when tests pass but the diff is noisy or over-complicated, as the final pass before review.
metadata:
  version: 1.0.0
  tags:
  - refactor
  - code-quality
  - workflow
  dependencies:
  - verification-before-completion
---

# Code Cleanup

## When to Use

Tests/build/typecheck pass but the diff is clumsy; feature works but has duplication, over-nesting, dead code, awkward naming; final simplification before review/merge; "broken window" needs boarding up.

## When NOT to Use

Behavior is broken or unverified; "cleanup" is cover for redesign; cleanup spreads to unrelated files; can't prove nothing broke.

## Core Principle

**Lock behavior first. Then simplify. Then re-verify.** Sequence: behavior locked → simplify → re-verify nothing changed. If any step fails, stop.

## Workflow

1. **Lock behavior.** Run the relevant tests + typecheck + lint. Save the output. This is your "before" baseline.
2. **Identify smell.** Use `fallow` (if available) for dead code, dupes, complexity. Otherwise: read the diff, mark spots that feel off.
3. **Simplify, in order:**
   - **Delete** (dead code, comments that restate, unused exports) — easiest, highest impact
   - **Rename** (clearer names, remove prefixes/suffixes) — cheap, high signal
   - **Extract** (a variable, a helper) — only if nameable and reused
   - **Inline** (a one-use wrapper) — only if the wrapper adds no clarity
   - **Restructure** (split a function, lift a conditional) — last resort, highest risk
4. **Re-verify.** Same tests, same typecheck, same lint. Outputs match "before" baseline.
5. **Diff review.** Anything outside the cleanup scope? Split it out.

## Anti-Simplification Patterns

- Adding an abstraction for "future use" — speculative, not cleanup
- "Improving" the architecture under cover of "cleanup" — separate change
- Renaming things the user named (breaks their mental model)
- "Fixing" unrelated lint warnings in the same diff
- Reformatting the whole file (no behavior change, but noise in diff)

## Red Flags

Cleanup before tests pass (can't prove nothing broke); "I just want to refactor this"; expanding into unrelated files; "while I'm here" fixes; deleting without checking consumers; tests deleted (not the cleanup target); reformatting the whole file; rename of public API; no baseline saved; re-verify skipped; "we'll add tests after" / "I'll write tests for the new structure later" (the tests are how you prove nothing broke).

## Anti-rationalization

| Shortcut the model reaches for | Why it fails here |
|---|---|
| "I'll simplify and fix the bug together" | Coupling them is scope creep; lock behavior first, simplify second — or you can't tell which broke. |
| "The diff is already noisy, a bit more won't matter" | Noise is the signal to stop + ship what's locked, not to add more. |
| "It's obviously simpler" | Obvious-to-you isn't verified; re-run the behavior test after simplifying. |

## Self-Quiz

- Is behavior locked (tests + typecheck + lint passing before)?
- Is each change *deletion or simplification*, not addition?
- Did I re-run the same checks after, and compare to baseline?
- Are all changes scoped to what was actually noisy?
- Did I avoid renaming public APIs or restructuring unrelated code?
