Verified against Cursor · 2026-08-10
Force Cursor's Agent mode to prove a refactor changed nothing observable before calling it done
A pure-structure refactor brief that requires a characterization test capturing current behavior, bugs included, before any code moves, and forbids any change that isn't reachable purely by renaming, extracting, or reorganizing — so a refactor stays a refactor instead of quietly becoming an unannounced behavior change.
The prompt
Ready to copy — highlighted parts are example details you can swap.
REFACTOR TARGET lib/billing/invoiceGenerator.ts GOAL STRUCTURE Split the 400-line generateInvoice function into separate lineItemCalculator, taxCalculator, and formatter modules, each independently testable. KNOWN QUIRKS TO PRESERVE, NOT FIX Tax-exempt orders currently still show a $0.00 tax line instead of omitting it — downstream PDF template relies on that line always being present. This is a structural refactor only — the observable behavior of lib/billing/invoiceGenerator.ts must be identical before and after, for every input a real caller could pass. If reaching GOAL STRUCTURE would require changing behavior anywhere, stop and name the conflict rather than refactoring through it. STEP 1 — CAPTURE CURRENT BEHAVIOR Before touching any code, write characterization tests against the current implementation covering: Every branch in the current tax-calculation logic (domestic, international, tax-exempt), rounding behavior at each step, and the exact line-item ordering in the final output.. These tests describe what the code does today, not what it should do — if something looks like a bug, capture it as current behavior anyway and flag it separately in your notes. Do not silently correct it while writing the test; that would mean testing against a target you already changed. STEP 2 — REFACTOR Restructure the code toward GOAL STRUCTURE using only moves that don't change behavior — extract, inline, rename, reorder, split, merge. If getting to the target shape genuinely requires a behavior difference somewhere, stop before making it and say so instead of quietly deciding the tradeoff yourself. STEP 3 — CONFIRM Run the Step 1 characterization tests against the refactored code, unmodified. Every one must still pass exactly as written. If a test needs to change to keep passing, that is evidence behavior changed, not evidence the test was wrong — report it as a break, not as a test fix. WHAT NOT TO DO - Do not fix a bug you flagged in Step 1 as part of this pass, even though you're already in the code. - Do not rename anything not required to reach GOAL STRUCTURE just because you noticed a better name while in there — a drive-by rename is still an unannounced change to anyone reviewing this diff by intent, not by line count. - Do not delete or loosen a characterization test that turns out to be inconvenient after the refactor; if it fails, the refactor is the thing in question, not the test. WHEN DONE State the number of characterization tests written, the number passing unmodified after the refactor, and every flagged-but-not-fixed quirk as a separate follow-up item, not folded into this change.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
A model asked to refactor code will often quietly conflate "restructure" with "improve," because its training rewards output that looks better along every visible axis at once, and fixing a bug or renaming an awkward variable it notices along the way reads as strictly positive — but a refactor's entire value as a reviewable unit of work depends on the diff being behavior-neutral, and any snuck-in fix inside that diff makes it impossible for a reviewer to tell which lines are structural and which quietly changed what the system does. Requiring characterization tests written against current behavior, bugs and all, before any code moves converts the existing contract from something trusted to memory into something pinned down in an executable, checkable form, and explicitly instructing the model to capture a known bug as current behavior rather than fix it while it's right there closes the most common version of this conflation, where a genuine correctness fix hides inside what's presented as a pure restructure and becomes essentially unreviewable as a result, since a reviewer scanning a "just a refactor" diff has no reason to scrutinize logic for correctness the way they would a feature change. The instruction that a failing characterization test after refactor means the refactor is wrong, not the test, closes a related and easy-to-miss loophole: a model under pressure to make its own tests pass again will edit the test if that's the path of least resistance, which quietly redefines "unchanged" as whatever the new code happens to produce rather than what the old code actually did — the entire point of writing the test first is that it isn't allowed to move once behavior is supposed to be fixed.
What you get back
Step 1: wrote 14 characterization tests covering domestic/international/tax-exempt branches, rounding at each subtotal step, and line-item ordering. Flagged: tax-exempt orders emit a $0.00 tax line rather than omitting it — captured as-is per KNOWN QUIRKS, not fixed.
Step 2: split generateInvoice into lineItemCalculator.ts, taxCalculator.ts, and formatter.ts. No behavior-changing moves made; one planned extraction (memoizing the tax lookup) was skipped because it would have changed rounding order — flagged instead of made.
Step 3: all 14 characterization tests pass unmodified against the refactored code. 14/14 passing, 0 changed, 1 quirk logged as a separate follow-up (not fixed here).
Verified against
Cursor 2.2 · 2026-08-10
Changelog
- 2026-08-10 — Initial publish, verified against Cursor 2.2 Agent mode on a structural refactor with characterization tests.
Need this built into your business?
If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.
EXPLORE CUSTOM SOFTWARE
