Install
openclaw skills install @dennisrongo/safe-refactorExecute 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 task-executor).
openclaw skills install @dennisrongo/safe-refactorExecute 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.
improve-codebase-architecture produced an approved deepening candidate and the user says "do it".Do not auto-trigger for changes meant to alter behavior — bug fixes, feature work, "improve this error message" — those are 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.
file:line. Contract items with no covering test get characterization tests before any restructuring begins (per write-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.getUser to fetchUser; build is green, done." — the build can't see the "getUser" string in the route table.getUser returns 2 hits: a CHANGELOG entry (historical, left) and routes.json:14 (updated)."expect(total).toBe(107.10) to 109.31 mid-refactor because "the new rounding is more correct."await, a widened catch, changed default parameter, iteration-order dependence, log lines something might parse. Report: contract (each item → covering test), steps taken, grep results for renames, and anything intentionally left un-refactored with a one-line why.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.
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)?
git bisect stays useful and reviewers can verify "no behavior change" structurally. Use conventional-commits (refactor: type) when committing.think-like-fable throughout: 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.