refactor
Restructure existing code without changing its behavior, through a test-gated refactoring loop: a named target, a green suite over that target before any edit, a planned sequence of small named refactorings, and the full suite re-run after every step. Use when the user wants to r
Install
npx skills add https://github.com/testdouble/han/tree/main/han-coding/skills/refactor
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install testdouble-han@llmmart
git clone https://github.com/testdouble/han.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole testdouble/han collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Project Context
- git installed: !
which git 2>/dev/null || echo "not installed" - current branch: !
git branch --show-current 2>/dev/null || echo unknown - working tree: !
git status --porcelain 2>/dev/null | head -5 - CLAUDE.md: !
find . -maxdepth 1 -name "CLAUDE.md" -type f - project-discovery.md: !
find . -maxdepth 3 -name "project-discovery.md" -type f - personal config directory: !
bash "${CLAUDE_PLUGIN_ROOT}/scripts/han-config-dir.sh" 2>/dev/null || echo "$HOME/.claude" - project .han/config.md: !
cat .han/config.md 2>/dev/null || echo ""
As your first action, use the Read tool on .han/config.md inside the personal config directory path above. A read
that returns no file is no personal configuration: continue silently. When that file or the project .han/config.md
probe supplies content, apply it per config-rule.md, which governs precedence
between the two files, relative-path resolution, and what to do with a file that reads but cannot be used.
Constraints (read before anything else)
This skill restructures existing production and test code in your working tree. It is an execution skill, not a document generator. These constraints shape every step and override any instinct to move faster. The canon they derive from, with provenance, is in references/refactoring-discipline.md; pull that reference when a step needs the full rule or a step feels off.
- Behavior preservation is the definition. A refactoring changes internal structure without changing observable behavior. A change that alters behavior is not a refactoring done badly; it is not a refactoring at all. When a planned step turns out to require a behavior change, it leaves this skill's scope: defer it with a note naming the behavior change it needs.
- Tests are the license to refactor. No edit until the full suite has been run, observed green, and the target's behavior is covered. If coverage of the target cannot be established, stop and offer the characterization path in the reference; never refactor blind.
- Small named steps, green to green. Each step is one named refactoring with a bounded mechanic (extract function, rename, move, inline, and so on), and the suite runs after every step. A red suite after a step means revert the step, not patch forward.
- The declared scope is a contract. The target named in Step 1 bounds every edit. When a step starts pulling in files outside that scope, stop, report the spread, and let the user re-scope. Spreading edits are how a refactoring silently becomes a rewrite.
- Never alongside an active tdd loop. If the working tree shows a red-green cycle in flight (failing tests, a
half-implemented behavior), do not run: the refactor step of
/tddowns cleanup inside the loop, and restructuring while a test is red violates the two-hats rule both skills share. - Refactor-only changes. No behavior fixes, no features, no drive-by bug fixes, even when you spot one. Record what you found and leave it. If the user asks for commits, each commit contains refactoring only.
- YAGNI governs the plan. Apply ../../references/yagni-rule.md: every refactoring in the plan needs evidence the code has a reason to change (a review finding, named duplication, a confusing read documented by the user, upcoming work in that area). Removing duplication is the job; adding speculative abstraction, configuration, or indirection is not. Defer evidence-free items with a reopen trigger.
Refactor
Step 1: Bind the Target and Resolve Project Config
Bind the target. Resolve the request to a named target: specific files or directories, a named code smell in a named
place, or the refactoring findings in a provided document (a /code-review report, an /architectural-analysis report,
or equivalent). When a findings document is given, extract only the refactoring-shaped findings (structural suggestions,
duplication, naming, coupling) and record each finding's ID so the summary can trace back to it. If the request is
open-ended ("clean up the codebase", "improve quality") with no named target, ask the user for one before doing
anything: open-ended refactoring runs are the documented failure mode this skill exists to avoid, and a wrong guess here
burns the whole run.
Resolve commands. Read CLAUDE.md's ## Project Discovery section for the test command (under
### Commands and Tests), the lint command, the build command, language, and framework. If absent, fall back to
project-discovery.md. If still absent, run ${CLAUDE_SKILL_DIR}/scripts/detect-refactor-context.sh and parse its
output for git state and manifest-inferred commands. A missing test command is a hard blocker: exhaust inference, then
ask the user, because this skill cannot run without a way to verify behavior. Also note any type-check command the
project has; where one exists it runs alongside the tests as a second behavior-preservation check.
Resolve standards and decisions. Resolve the coding-standards directory and ADR directory the same way: CLAUDE.md's
## Project Discovery section, then project-discovery.md, then Glob defaults (docs/, docs/adr/,
docs/coding-standards/, docs/decisions/). Also check CLAUDE.md and AGENTS.md for inline standards. Read the
standards and ADRs whose titles, paths, or one-line summaries indicate they govern the target area; cap at five
documents. The target's restructured form must conform to these. If none exist, state that plainly and infer conventions
from the surrounding code.
Step 2: Establish the Safety Net
Check the tree. If working tree in Project Context shows failing mid-cycle work or the user describes an in-flight
tdd loop on this code, stop and say why (the never-alongside-tdd constraint). Uncommitted but complete work is fine;
recommend committing it first so refactoring diffs stay clean, then proceed.
Run the full suite. Run the resolved test command with Bash. Paste the runner's summary line. If anything is
red, stop: a red suite is not a license to refactor. Report the failures and recommend fixing them first (via
/investigate or /tdd), or re-scoping the target away from the broken area.
Establish coverage of the target. Confirm the target's observable behavior is exercised by the suite: find the tests that drive the target (Glob and Grep for the target's public symbols in test files), and run them scoped if the runner supports it. State plainly which behaviors are covered and which are not. If the target has no meaningful coverage, stop and offer two ways forward, and wait for the user's choice: narrow the target to the covered part, or write characterization tests first using the protocol in references/refactoring-discipline.md. Characterization tests pin current observed behavior (including current bugs) and are a lower-confidence net than intent-written tests; say so in the report.
Step 3: Plan the Sequence Before Editing
Plan the whole run before the first edit. Produce a numbered refactoring plan where each item is:
- One named refactoring (the vocabulary is in the reference; use the project's language idioms, not Java mechanics), applied to a named place.
- The evidence for it. The finding ID, the duplicated code, the standard or ADR it brings the code into conformance with, or the user's stated pain. Items without evidence are deferred per the YAGNI constraint, with a reopen trigger, never silently dropped.
- Its expected blast radius. The files the step should touch. This is what the scope stop-rule in Step 4 checks against.
Order the plan so each step leaves the code releasable: small, independent steps first, dependent steps after the steps they need. Report the plan to the user, then continue immediately; this is a report, not a gate. The one exception: if the request explicitly asks to review or approve the plan first, wait for approval.
Step 4: Execute, One Named Refactoring at a Time
Take the plan items in order. For each:
Make the one change. Apply the single named refactoring, touching only the files in its declared blast radius. Stay in the refactoring hat: no behavior fixes, no extras spotted along the way (record them for the summary instead).
Run the full suite (and the type-check command where one was resolved). Paste the runner's summary line. Paste full output only when something fails or looks unexpected.
Green: cross the item off and move to the next.
Running collaboratively. When the request asks to review each named refactoring as it lands, which is what
pairingdoes when it hands work here, stop at this point and hand control back instead of continuing. Present the stop in the shape collaborative-stop-rule.md specifies. Absent such a request, continue as below; an ordinary invocation is unchanged.Red: revert this step. When git is available and the tree was clean at start,
git checkout/git restorethe touched files. When git is absent, or the tree was already dirty at start, undo the edits directly instead, so reverting this step does not discard the user's other work. Do not patch forward over a red suite; a failed step means the mechanic was unsafe or the coverage was thinner than it looked. Diagnose, then either retry with a smaller step or defer the item with what you learned.Stop rules. If the step needed files outside its declared blast radius, or the only way to make it work changes observable behavior, or two consecutive plan items have been reverted: stop, report where things stand (everything already applied is green and stands), and let the user re-scope.
If the user asked for commits, commit after each green step or each logical group of green steps, message naming the refactoring applied, refactoring only.
Step 5: Final Verification and Summary
Run the full test suite, the lint command, and the build command from Step 1. Paste the summary line from each. If lint or build fails on code this skill touched, fix it and re-run; that is in scope.
Summarize for the user:
- Each refactoring applied, named, with the evidence it rested on (finding IDs from the source report where one was used) and the files it touched.
- Deferred items: the YAGNI deferrals with reopen triggers, any items deferred because they required behavior changes, and any stop-rule exits.
- Anything spotted but deliberately left alone (bugs, smells outside scope), so it can feed
/issue-triageor the next/code-review. - Which coding standards and ADRs the restructured code now conforms to.
- Final test, lint, and build status, with output shown, not asserted.
- If characterization tests were written, say so and recommend replacing them with intent-written tests over time.
Files (han)
-
references
-
refactoring-discipline.md 6.8 KB
# Refactoring Discipline: the Canon The rules the `/refactor` skill enforces, with their provenance. Pull this file when a step needs the full rule, when a run feels off, or when the user asks why the skill refused to proceed. ## The definition Refactoring (noun): a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior. Refactoring (verb): to restructure software by applying a series of refactorings without changing its observable behavior. Both are Fowler's definitions; the formal version (Opdyke, 1992) is that for the same set of input values, the resulting set of output values is the same before and after the change. Two consequences the skill enforces: - A "refactoring" that changes behavior is a defect, not a refactoring. When a planned step cannot be completed without changing behavior, the step leaves the skill's scope and is deferred with a note. - The system stays working the whole time. Fowler's test for real refactoring is that the system is never broken for more than a few minutes. That is why the suite runs after every step, not at the end. ## The preconditions 1. **A green suite covering the target, before anything changes.** Tests are the only practical proof of behavior preservation. The majority practitioner position is unambiguous: no tests, no refactoring. Coverage percentage is not the bar; the bar is that the target's observable behaviors are exercised. 2. **Green to green, step by step.** The suite passes before a step and after it. A red suite after a step means the step gets reverted, never patched forward. Reverting is cheap precisely because steps are small. 3. **A type checker or linter, where the project has one, runs alongside the tests.** Static checks catch a class of unsafe edits (broken call sites, type violations) that behavior tests can miss. Tests plus static checks are the dual oracle. ## Named refactorings as vocabulary Each plan item is one named refactoring: extract function, inline function, rename, move function or field, extract variable, replace conditional with polymorphism, replace temp with query, and the rest of the catalog at refactoring.com/catalog. The names matter because a named refactoring has a bounded, known mechanic, and the evidence on agent-driven refactoring is blunt: naming the specific refactoring dramatically outperforms open-ended "improve this code" instructions, which identify few real opportunities and tend to make structure worse, not better. The catalog is a vocabulary, not a straitjacket. It is Java-flavored in origin; apply the intent through the project's language idioms (a React component extraction, a Python module split, a Go interface narrowing are all catalog moves wearing local clothes). What is not allowed is an unnamed mega-step ("restructure the module") that has no bounded mechanic to verify. ## Scope rules - **The declared target bounds every edit.** Changes spreading beyond the initial subject are the line between refactoring and rewriting. Crossing it is a stop-and-report, not a judgment call to keep going. - **Don't refactor code with no reason to change.** The high-value targets are code that is both complex and frequently changed. Messy code that works, is never read, and is never touched repays nothing. This is the YAGNI evidence gate applied to refactoring: each plan item needs a reason (a finding, duplication, a documented confusing read, upcoming work here). - **Aggressive sweeps lose to conservative steps.** The empirical record on agent refactoring shows aggressive wide passes resolve some problems while introducing more than they fix; tightly scoped conservative passes come out net positive. When in doubt, do less. ## The characterization-test protocol (for uncovered targets) When the target has no meaningful coverage and the user chooses this path rather than narrowing the target (Feathers' technique for legacy code, where legacy code is defined as code without tests): 1. Identify the target's public entry points (the seams). 2. For each, write tests that capture what the code actually does now: call it with representative and edge inputs, assert the actual observed outputs. Run each test, observe it pass, and lock the observed value in. Snapshot-style assertions are acceptable here. 3. Do not fix bugs the characterization reveals. The tests pin current behavior, bugs included; a behavior change, even a fix, is out of scope. Record discovered bugs in the summary for `/issue-triage`. 4. Label these tests as characterization tests in their names or comments, and say in the final summary that the safety net is lower-confidence than intent-written tests: it proves "unchanged", not "correct", and its strength depends on input coverage you chose. Recommend replacing them with intent-written tests as the area gets real work. ## Failure modes to catch yourself in - **Tangling.** Slipping a behavior fix, a feature crumb, or a drive-by bug fix into a refactoring step. This is the most common agent failure in the field record (over half of observed agent refactorings are tangled into unrelated changes). The discipline: record what you spotted, leave it, surface it in the summary. - **Scope drift dressed as momentum.** "While I'm here" is the start of a rewrite. The blast radius declared in the plan is the check; the stop rule is the enforcement. - **Patching forward over a red.** A failed step means the mechanic was unsafe or coverage was thin. Reverting preserves the green-to-green invariant; patching forward abandons it and converts the run into unverified rewriting. - **Refactoring to taste.** Restructuring with no finding, no duplication, no standard, and no stated pain behind it. The YAGNI gate exists to catch exactly this; aesthetic-only items are deferred. - **Trusting tests generated from the code being refactored.** A test written by reading the implementation and asserting what it does is a characterization test and carries that label's lower confidence; presenting it as a behavioral spec overstates the net. ## Provenance The rules above are grounded in the refactoring literature and in the empirical record on agent-driven refactoring. The full evidence trail, with sources and validation, is in the Han repository at `docs/research/refactor-skill-research.md`. Primary anchors: Martin Fowler, _Refactoring_ (definition, catalog, two hats, workflows, scope); William Opdyke's 1992 thesis (formal behavior preservation); Michael Feathers, _Working Effectively with Legacy Code_ (characterization tests, seams); Kent Beck ("make the change easy, then make the easy change"); and the 2024-2026 empirical studies of LLM and agent refactoring (named targets over open-ended prompts, plan-then-execute, incremental verification gates, conservative over aggressive scope).
-
-
scripts
-
detect-refactor-context.sh 3.2 KB
#!/usr/bin/env bash # Detect git state and infer test/lint/build commands from project manifests. # Run ONCE in Step 1, only when CLAUDE.md / project-discovery.md did not # resolve the commands. The per-step suite runs in the refactoring loop do NOT # use this script — they call the resolved test command directly. # # Inference is a best-effort fallback. Emitted commands are suggestions for the # user to confirm, not authority. Output is line-oriented key: value pairs. # --- git state --------------------------------------------------------------- if command -v git &>/dev/null && git rev-parse --is-inside-work-tree &>/dev/null; then echo "git-available: true" BRANCH=$(git branch --show-current) echo "branch: ${BRANCH:-none}" if git symbolic-ref --short refs/remotes/origin/HEAD &>/dev/null; then echo "default-branch: $(git symbolic-ref --short refs/remotes/origin/HEAD)" else echo "default-branch: none" fi else echo "git-available: false" echo "branch: none" echo "default-branch: none" fi # --- manifest-inferred commands ---------------------------------------------- # First match wins per category. These are suggestions; the user confirms. TEST_CMD="" LINT_CMD="" BUILD_CMD="" if [ -f package.json ]; then echo "manifest: package.json" grep -q '"test"[[:space:]]*:' package.json && TEST_CMD="npm test" grep -q '"lint"[[:space:]]*:' package.json && LINT_CMD="npm run lint" grep -q '"build"[[:space:]]*:' package.json && BUILD_CMD="npm run build" fi if [ -z "$TEST_CMD" ] && { [ -f pyproject.toml ] || [ -f pytest.ini ] || [ -f setup.cfg ] || [ -f tox.ini ]; }; then echo "manifest: python (pyproject/pytest)" TEST_CMD="pytest" fi if [ -z "$TEST_CMD" ] && [ -f go.mod ]; then echo "manifest: go.mod" TEST_CMD="go test ./..." BUILD_CMD="go build ./..." fi if [ -z "$TEST_CMD" ] && [ -f Cargo.toml ]; then echo "manifest: Cargo.toml" TEST_CMD="cargo test" BUILD_CMD="cargo build" fi if [ -z "$TEST_CMD" ] && { [ -f Gemfile ] || [ -f Rakefile ] || [ -d spec ]; }; then echo "manifest: ruby (Gemfile/Rakefile)" if [ -d spec ]; then TEST_CMD="bundle exec rspec"; else TEST_CMD="bundle exec rake test"; fi fi if [ -z "$TEST_CMD" ] && [ -f mix.exs ]; then echo "manifest: mix.exs" TEST_CMD="mix test" fi if [ -z "$TEST_CMD" ] && { [ -f pom.xml ]; }; then echo "manifest: pom.xml" TEST_CMD="mvn test" BUILD_CMD="mvn package" fi if [ -z "$TEST_CMD" ] && { [ -f build.gradle ] || [ -f build.gradle.kts ]; }; then echo "manifest: gradle" TEST_CMD="gradle test" BUILD_CMD="gradle build" fi if [ -z "$TEST_CMD" ] && [ -n "$(find . -maxdepth 3 -name '*.csproj' -print -quit 2>/dev/null)" ]; then echo "manifest: dotnet (.csproj)" TEST_CMD="dotnet test" BUILD_CMD="dotnet build" fi # A Makefile with a test target is a common override regardless of language. if [ -f Makefile ] && grep -qE '^test:' Makefile; then echo "manifest: Makefile (test target)" [ -z "$TEST_CMD" ] && TEST_CMD="make test" grep -qE '^lint:' Makefile && [ -z "$LINT_CMD" ] && LINT_CMD="make lint" grep -qE '^build:' Makefile && [ -z "$BUILD_CMD" ] && BUILD_CMD="make build" fi echo "inferred-test-command: ${TEST_CMD:-none}" echo "inferred-lint-command: ${LINT_CMD:-none}" echo "inferred-build-command: ${BUILD_CMD:-none}"
-
-
SKILL.md 12.2 KB
--- name: refactor description: > Restructure existing code without changing its behavior, through a test-gated refactoring loop: a named target, a green suite over that target before any edit, a planned sequence of small named refactorings, and the full suite re-run after every step. Use when the user wants to refactor, restructure, clean up, simplify, or improve the design of existing code, or to apply refactoring recommendations from a code-review or architectural-analysis report. This skill changes code; it does not review code (use code-review), assess architecture (use architectural-analysis), plan a multi-module restructure before executing it (use plan-a-change), map domain boundaries before restructuring along them (use ddd-analysis), or build new behavior test-first (use tdd). Do not use it on code inside an active tdd loop; the refactor step of tdd owns that cleanup. Runs its planned sequence to completion without pausing for review; to review each named refactoring as it lands, use pairing. argument-hint: "[file, module, named smell, or a path to review findings]" allowed-tools: Read, Write, Edit, Glob, Grep, Bash(git *), Bash(find *), Bash(npm *), Bash(npx *), Bash(pnpm *), Bash(yarn *), Bash(pytest *), Bash(python3 *), Bash(go *), Bash(cargo *), Bash(make *), Bash(bundle *), Bash(rake *), Bash(bash "${CLAUDE_PLUGIN_ROOT}/scripts/han-config-dir.sh") --- ## Project Context - git installed: !`which git 2>/dev/null || echo "not installed"` - current branch: !`git branch --show-current 2>/dev/null || echo unknown` - working tree: !`git status --porcelain 2>/dev/null | head -5` - CLAUDE.md: !`find . -maxdepth 1 -name "CLAUDE.md" -type f` - project-discovery.md: !`find . -maxdepth 3 -name "project-discovery.md" -type f` - personal config directory: !`bash "${CLAUDE_PLUGIN_ROOT}/scripts/han-config-dir.sh" 2>/dev/null || echo "$HOME/.claude"` - project .han/config.md: !`cat .han/config.md 2>/dev/null || echo ""` As your first action, use the Read tool on `.han/config.md` inside the `personal config directory` path above. A read that returns no file is no personal configuration: continue silently. When that file or the `project .han/config.md` probe supplies content, apply it per [config-rule.md](../../references/config-rule.md), which governs precedence between the two files, relative-path resolution, and what to do with a file that reads but cannot be used. ## Constraints (read before anything else) This skill restructures existing production and test code in your working tree. It is an execution skill, not a document generator. These constraints shape every step and override any instinct to move faster. The canon they derive from, with provenance, is in [references/refactoring-discipline.md](./references/refactoring-discipline.md); pull that reference when a step needs the full rule or a step feels off. - **Behavior preservation is the definition.** A refactoring changes internal structure without changing observable behavior. A change that alters behavior is not a refactoring done badly; it is not a refactoring at all. When a planned step turns out to require a behavior change, it leaves this skill's scope: defer it with a note naming the behavior change it needs. - **Tests are the license to refactor.** No edit until the full suite has been run, observed green, and the target's behavior is covered. If coverage of the target cannot be established, stop and offer the characterization path in the reference; never refactor blind. - **Small named steps, green to green.** Each step is one named refactoring with a bounded mechanic (extract function, rename, move, inline, and so on), and the suite runs after every step. A red suite after a step means revert the step, not patch forward. - **The declared scope is a contract.** The target named in Step 1 bounds every edit. When a step starts pulling in files outside that scope, stop, report the spread, and let the user re-scope. Spreading edits are how a refactoring silently becomes a rewrite. - **Never alongside an active tdd loop.** If the working tree shows a red-green cycle in flight (failing tests, a half-implemented behavior), do not run: the refactor step of `/tdd` owns cleanup inside the loop, and restructuring while a test is red violates the two-hats rule both skills share. - **Refactor-only changes.** No behavior fixes, no features, no drive-by bug fixes, even when you spot one. Record what you found and leave it. If the user asks for commits, each commit contains refactoring only. - **YAGNI governs the plan.** Apply [../../references/yagni-rule.md](../../references/yagni-rule.md): every refactoring in the plan needs evidence the code has a reason to change (a review finding, named duplication, a confusing read documented by the user, upcoming work in that area). Removing duplication is the job; adding speculative abstraction, configuration, or indirection is not. Defer evidence-free items with a reopen trigger. # Refactor ## Step 1: Bind the Target and Resolve Project Config **Bind the target.** Resolve the request to a named target: specific files or directories, a named code smell in a named place, or the refactoring findings in a provided document (a `/code-review` report, an `/architectural-analysis` report, or equivalent). When a findings document is given, extract only the refactoring-shaped findings (structural suggestions, duplication, naming, coupling) and record each finding's ID so the summary can trace back to it. If the request is open-ended ("clean up the codebase", "improve quality") with no named target, ask the user for one before doing anything: open-ended refactoring runs are the documented failure mode this skill exists to avoid, and a wrong guess here burns the whole run. **Resolve commands.** Read CLAUDE.md's `## Project Discovery` section for the test command (under `### Commands and Tests`), the lint command, the build command, language, and framework. If absent, fall back to `project-discovery.md`. If still absent, run `${CLAUDE_SKILL_DIR}/scripts/detect-refactor-context.sh` and parse its output for git state and manifest-inferred commands. A missing test command is a hard blocker: exhaust inference, then ask the user, because this skill cannot run without a way to verify behavior. Also note any type-check command the project has; where one exists it runs alongside the tests as a second behavior-preservation check. **Resolve standards and decisions.** Resolve the coding-standards directory and ADR directory the same way: CLAUDE.md's `## Project Discovery` section, then `project-discovery.md`, then Glob defaults (`docs/`, `docs/adr/`, `docs/coding-standards/`, `docs/decisions/`). Also check `CLAUDE.md` and `AGENTS.md` for inline standards. Read the standards and ADRs whose titles, paths, or one-line summaries indicate they govern the target area; cap at five documents. The target's restructured form must conform to these. If none exist, state that plainly and infer conventions from the surrounding code. ## Step 2: Establish the Safety Net **Check the tree.** If `working tree` in Project Context shows failing mid-cycle work or the user describes an in-flight tdd loop on this code, stop and say why (the never-alongside-tdd constraint). Uncommitted but complete work is fine; recommend committing it first so refactoring diffs stay clean, then proceed. **Run the full suite.** Run the resolved test command with Bash. **Paste the runner's summary line.** If anything is red, stop: a red suite is not a license to refactor. Report the failures and recommend fixing them first (via `/investigate` or `/tdd`), or re-scoping the target away from the broken area. **Establish coverage of the target.** Confirm the target's observable behavior is exercised by the suite: find the tests that drive the target (Glob and Grep for the target's public symbols in test files), and run them scoped if the runner supports it. State plainly which behaviors are covered and which are not. If the target has no meaningful coverage, stop and offer two ways forward, and wait for the user's choice: narrow the target to the covered part, or write characterization tests first using the protocol in [references/refactoring-discipline.md](./references/refactoring-discipline.md). Characterization tests pin current observed behavior (including current bugs) and are a lower-confidence net than intent-written tests; say so in the report. ## Step 3: Plan the Sequence Before Editing Plan the whole run before the first edit. Produce a numbered refactoring plan where each item is: - **One named refactoring** (the vocabulary is in the reference; use the project's language idioms, not Java mechanics), applied to a named place. - **The evidence for it.** The finding ID, the duplicated code, the standard or ADR it brings the code into conformance with, or the user's stated pain. Items without evidence are deferred per the YAGNI constraint, with a reopen trigger, never silently dropped. - **Its expected blast radius.** The files the step should touch. This is what the scope stop-rule in Step 4 checks against. Order the plan so each step leaves the code releasable: small, independent steps first, dependent steps after the steps they need. Report the plan to the user, then continue immediately; this is a report, not a gate. The one exception: if the request explicitly asks to review or approve the plan first, wait for approval. ## Step 4: Execute, One Named Refactoring at a Time Take the plan items in order. For each: 1. **Make the one change.** Apply the single named refactoring, touching only the files in its declared blast radius. Stay in the refactoring hat: no behavior fixes, no extras spotted along the way (record them for the summary instead). 2. **Run the full suite** (and the type-check command where one was resolved). **Paste the runner's summary line.** Paste full output only when something fails or looks unexpected. 3. **Green: cross the item off** and move to the next. **Running collaboratively.** When the request asks to review each named refactoring as it lands, which is what `pairing` does when it hands work here, stop at this point and hand control back instead of continuing. Present the stop in the shape [collaborative-stop-rule.md](../../references/collaborative-stop-rule.md) specifies. Absent such a request, continue as below; an ordinary invocation is unchanged. 4. **Red: revert this step.** When git is available and the tree was clean at start, `git checkout`/`git restore` the touched files. When git is absent, or the tree was already dirty at start, undo the edits directly instead, so reverting this step does not discard the user's other work. Do not patch forward over a red suite; a failed step means the mechanic was unsafe or the coverage was thinner than it looked. Diagnose, then either retry with a smaller step or defer the item with what you learned. 5. **Stop rules.** If the step needed files outside its declared blast radius, or the only way to make it work changes observable behavior, or two consecutive plan items have been reverted: stop, report where things stand (everything already applied is green and stands), and let the user re-scope. If the user asked for commits, commit after each green step or each logical group of green steps, message naming the refactoring applied, refactoring only. ## Step 5: Final Verification and Summary Run the full test suite, the lint command, and the build command from Step 1. **Paste the summary line from each.** If lint or build fails on code this skill touched, fix it and re-run; that is in scope. Summarize for the user: - Each refactoring applied, named, with the evidence it rested on (finding IDs from the source report where one was used) and the files it touched. - Deferred items: the YAGNI deferrals with reopen triggers, any items deferred because they required behavior changes, and any stop-rule exits. - Anything spotted but deliberately left alone (bugs, smells outside scope), so it can feed `/issue-triage` or the next `/code-review`. - Which coding standards and ADRs the restructured code now conforms to. - Final test, lint, and build status, with output shown, not asserted. - If characterization tests were written, say so and recommend replacing them with intent-written tests over time.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.