safe-refactor
Skill1 starsChanged 3 months agoMITView on GitHub
Execute a behavior-preserving refactor with a proof of preservation — establishes a safety net first (existing tests or new characterization tests over every touched path), locks a baseline green run, then moves in small always-green steps where each step is one mechanical transformation, and treats any needed assertion change as a smuggled behavior change to surface, not fix. Use this skill whenever the user says "refactor this", "clean this up without changing behavior", "extract this into", "restructure this module", "rename this across the codebase", "inline this", "split this function/class", or "/safe-refactor" — even if they don't explicitly say "refactoring skill". Do not use for choosing WHAT to refactor (use improve-codebase-architecture) or for changes that are supposed to alter behavior (use plan-and-build).
- Deletes or force-pushes
26 other projects use a version of this.
Adopted by
Most recently taken up by
- sudeepgupta90/my-ai-marketplacetheir own version, first seen Nov 8, 2026
- mjlueck-hash/cavemantheir own version, first seen Nov 8, 2026
- dhruvsaxena1998/skillstheir own version, first seen Nov 8, 2026
- MMACABRE/SKILLStheir own version, first seen Nov 8, 2026
- pahlevikun/levistacktheir own version, first seen Nov 1, 2026
- Terryc21/sitreptheir own version, first seen Nov 1, 2026
Used with
Projects with it also use verify-and-stop, investigate-first and 10 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
JuliusBrusseeSkillverify-and-stop
Prove existing work meets acceptance conditions without expanding scope. Use for validation-only tasks, completion checks, focused gate runs, and last-mile proof.
Also in 8 projects that use this
JuliusBrusseeSkillinvestigate-first
Diagnose ambiguous failures before editing. Use for unknown causes, intermittent behavior, performance regressions, or investigations needing evidence-ranked hypotheses.
Also in 7 projects that use this
- +10 more from JuliusBrussee
Other versions26 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 26
- JuliusBrussee/caveman★ 118ksame name
- Terryc21/sitrep★ 50same name
- JPeetz/agent-skills★ 14same name
- yeaight7/agent-powerups★ 6same name
- dfedoryshchev/weftworks★ 5same name
- Linchi1997yeh/craftsman-suite★ 2same name
- nomdedev/agent-manager-template★ 2same name
- xingtu1996/dsh-xingtu-skills★ 2same name
- dhruvsaxena1998/skills★ 1same name
- jjsizemore/jjskills★ 1same name
16 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository38 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- api-contract-reviewSkill
- autopilotSkill
- backlog-plannerSkill
- codebase-explainerSkill
- code-reviewSkill
- context-dietSkill
- conventional-commitsSkill
- create-prSkill
- design-briefSkill
- diagnoseSkill
- dotnet-conventionsSkill
- dotnet-onion-apiSkill
- draw-optionsSkill
- e2e-verifySkill
- githubSkill
- goal-runnerSkill
- grill-with-docsSkill
- handoffSkill
- humanizerSkill
- improve-codebase-architectureSkill
- maestro-mobile-testSkill
- migration-safetySkill
- model-inventorySkill
- nextjs-app-routerSkill
- plan-and-buildSkill
- record-browser-testSkill
- regression-huntSkill
- security-reviewSkill
- ship-itSkill
- sql-reviewSkill
- task-executorSkill
- tauri-2-appSkill
- think-like-fableSkill
- upgrade-depsSkill
- worktree-sandboxSkill
- write-a-skillSkill
- write-testsSkill
The file
We split this skill into the rules it gives an agent
1 rule. 0 are common, 1 is distinctive, and 0 are very specific to this project.
0
Common
Most of the 24 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 71
If the safety-net phase reveals the code is nearly untestable, that finding may reorder the plan — sometimes the first refactor step must …
“Common” is measured: kept by at least 6 in 10 of the 24 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Safe Refactor
Execute a refactor as a sequence of proofs, not a rewrite. "Behavior-preserving" is a falsifiable claim: the observable behavior before and after must be identical, and this skill's job is to make that claim checkable at every step instead of asserting it once at the end. The completion of the chain: improve-codebase-architecture names the target, write-tests builds the net, this skill makes the move.
When to use this skill
- The user says "refactor this", "clean this up", "extract X into Y", "restructure without changing behavior", "rename across the codebase", "split this up", "inline this", "/safe-refactor".
improve-codebase-architectureproduced an approved deepening candidate and the user says "do it".- A feature task requires restructuring existing code before the new behavior can land (do the refactor as its own phase, this skill governs that phase only).
Do not auto-trigger for changes meant to alter behavior — bug fixes, feature work, "improve this error message" — those are plan-and-build / task-executor territory. If the user's "refactor" turns out to include behavior changes, split the work: refactor first under this skill, behavior change after, never interleaved.
Workflow
- Write the behavior contract. One short list: the observable surface of the code being moved — public functions/endpoints and their outputs, side effects (writes, emits, logs that anything consumes), error types thrown, and performance characteristics if anything depends on them. This list is what "preserved" means; everything not on it is fair game. If you can't write the list, you haven't read enough code yet — a claim about code you haven't opened is a hypothesis.
- Audit the safety net. For each item on the contract, find the test that would fail if it broke — grep the test tree, cite
file:line. Before writing any tests or touching any file, rungit status: if the working tree is not clean, stop and ask the user to commit or stash the existing changes first — this skill's reverts must never be able to touch work that isn't yours. Contract items with no covering test get characterization tests before any restructuring begins (perwrite-tests: pin actual behavior, prove each test can go red). No net, no refactor — this gate is not skippable, and "the change is simple" is not an exemption; simple changes with no net are how behavior drifts silently. - Lock the baseline. Run the full suite (single-run mode, never watch mode) and build; quote both summary lines. A result you didn't observe is "not run", never "passed". If anything is red before you start, stop and surface it — you cannot distinguish your breakage from pre-existing breakage on a red baseline.
- Plan the move as mechanical steps. Each step is ONE named transformation — extract function, move file, rename symbol, inline variable, introduce parameter, replace conditional with polymorphism — small enough that if the suite goes red, the cause is unambiguous. Order steps so each leaves the code compiling and the suite green. Present the step list and stop — do not begin step 5 until the user approves it; ask the commit question ("commit at each rollback point?") in the same message so both answers arrive in one round-trip.
- Execute step → verify → step. After every step: build + run the affected tests (full suite at least at every commit point), quote the result. Affected tests = the covering tests cited in step 2 for the contract items this step touches; if you cannot name that set for a step, run the full suite instead. Green → proceed. Red → the step is wrong; revert or fix the step, never the test. Revert means undoing only the files this step edited — targeted
git restore <file>...if the last good state is committed, manual re-edit otherwise (git restorerewinds a file past your earlier uncommitted steps too); nevergit reset --hardorgit checkout -- .. A red step gets one bounded fix attempt: read the actual error, change exactly one thing, retry once — a second red on the same step means revert the step and surface it; never keep patching until something goes green. Each step (or small coherent group) is a rollback point — ask once, up front, whether to commit at these points; committing without that yes is not authorized by this skill. - Renames are grep-verified, not assumed. After renaming or moving a symbol, grep for the old name repo-wide — including strings, configs, reflection sites, docs, and serialized names (JSON keys, DB column mappings, API routes). Zero hits or each remaining hit justified in one line. Dynamic-dispatch and reflection usages don't break the build; the grep is the only net they have. Run it repo-wide, no path or extension filters:
rg -n --hidden --glob '!.git' '<old-name>'(no ripgrep:grep -rn --exclude-dir=.git '<old-name>' .), from the repo root.- ❌ "Renamed
getUsertofetchUser; build is green, done." — the build can't see the"getUser"string in the route table. - ✅ "Renamed; repo grep for
getUserreturns 2 hits: a CHANGELOG entry (historical, left) androutes.json:14(updated)."
- ❌ "Renamed
- The assertion-change tripwire. If any step makes an existing test fail and the "fix" would be changing that test's assertion, expected value, or setup — stop. That is the definition of a behavior change wearing a refactor's clothes. Surface it: quote the old and new behavior, ask whether the change is intended. Only test changes allowed without asking: mechanical renames mirroring step 6, and import/path updates.
- ❌ Silently updating
expect(total).toBe(107.10)to109.31mid-refactor because "the new rounding is more correct." - ✅ "Step 4 changes the rounding point, which alters totals on multi-line invoices (old: 107.10, new: 109.31). That's a behavior change, not a refactor — proceed with old behavior preserved, or split this into a follow-up?"
- ❌ Silently updating
Final proof. Full suite + build, summaries quoted. Then re-read the complete diff once, adversarially, hunting for the behavior change you smuggled in without noticing — reordered side effects, a dropped
await, a widened catch, changed default parameter, iteration-order dependence, log lines something might parse. Report using this template — every line carries a tag:verified(observed this session),inferred(deduced from something observed) orassumed(neither):## Refactor proof Contract: <item> → <test file:line> — [verified|assumed] Baseline: <quoted suite summary> / <quoted build result> Steps: <n>. <transformation> → <quoted result> Rename greps: <old-name> → <hit count>, <justification per remaining hit> Left un-refactored: <item> — <one-line why> Assumptions: <what breaks if wrong>
Examples
Example 1: extract a module
User: "Pull the pricing logic out of OrderService into its own PricingEngine."
Claude: Writes the contract (3 public methods, the discount side effect, 2 error types), finds tests covering 2 of 3 methods, writes characterization tests for the third and proves them red-capable, locks baseline (112 passed, build green), then: extract class → move methods one at a time → re-point callers → delete originals, suite quoted green after each, old-name grep clean, final adversarial diff read confirms no reordered side effects.
Example 2: the smuggled behavior change
User: "Refactor this date-handling mess to use one formatter."
Claude: Mid-sequence, unifying formatters makes a test expecting "3/5/2026" fail because the shared formatter emits "03/05/2026". Stops at the tripwire, shows both outputs, asks: preserve the legacy format via a format parameter (pure refactor), or adopt the new format (behavior change, separate commit, may affect consumers)?
Anti-patterns
- ❌ Refactoring untested code because "it's a small change" — the net gate (step 2) exists precisely for the changes that feel too small to break anything.
- ❌ The big-bang rewrite: restructure everything, then see what's red. When five things break you can't attribute any of them.
- ❌ Fixing a red step by editing the test — the tripwire (step 7) exists because this is the single most common way behavior changes ship inside "refactors".
- ❌ Interleaving "while I'm in here" behavior improvements with the restructuring — one diff, two intents, zero reviewability. Queue them for after.
- ❌ Trusting the compiler to catch all breakage from a rename — strings, reflection, routes, and serialization don't compile-check.
- ❌ Reporting the refactor complete without quoting the final suite run — "not run" is the only honest label for an unobserved result.
- ❌ Refactoring toward a pattern the repo doesn't use because it's considered best practice — match the codebase's existing idiom; consistency is a feature.
- ✅ Contract → net → baseline → small proven steps → grep-verified renames → adversarial final read.
Notes
- Commit hygiene: keep refactor commits free of behavior changes so
git bisectstays useful and reviewers can verify "no behavior change" structurally. Match the project's commit convention first (project CLAUDE.md, then the last ~20git log --onelinesubjects); only if no convention exists andconventional-commitsis installed, use itsrefactor:type. - If the safety-net phase reveals the code is nearly untestable, that finding may reorder the plan — sometimes the first refactor step must be the minimal seam that makes testing possible (extract the untestable I/O, pin the rest). Say so rather than silently expanding scope.
- Apply
think-like-fablethroughout: the contract is the decomposition into independently checkable pieces, every green claim is re-derived by running (never recognized), and the final adversarial read is attacking your own conclusion before handing it over.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

