refactor-safely

Restructure existing code safely without changing externally observable behavior. Composes context, design, architecture, code quality, and testing guardrails into a characterization-first refactoring

SKILL.md

GitHub

Refactor Safely

Required Skills

Load these skills based on refactor scope (see Steps 3, 5, 6 for conditional use):

  1. framework:knowledge-priming -- Load project context so the refactor grounds in the real codebase. (always)
  2. framework:context-anchoring -- Find and load the feature's context doc; capture approved plans and decisions in it. (always)
  3. framework:learning-harvest -- Load prior operational learnings at session start; harvest new ones at session end. (always)
  4. framework:collaborative-judgment -- Surface trade-offs in structure, seams, and migration sequence instead of silently assuming. (always)
  5. framework:clean-code -- Readability, responsibility boundaries, local craft. (always)
  6. framework:test-quality -- Characterization tests and the safety net. (always)
  7. framework:design-first -- Target-structure planning for significant structural changes. (conditional: Step 3)
  8. framework:architecture -- Layer placement and dependency direction. (conditional: responsibilities move across layers in Steps 5/6)
  9. framework:domain-driven-design -- Aggregate boundaries and domain behavior. (conditional: domain rules move or sharpen in Step 6)
  10. framework:secure-coding -- Trust bounds, authorization, sensitive data handling. (conditional: security-sensitive code touched in Step 6)

Additional Instructions

STOP: Before Step 1, read additional_instructions.refactor_safely (one line or a list) from .lattice/config.yaml. No file or no key → skip silently.

When lines are found:

  1. Show them verbatim under "Additional instructions (from config):".
  2. Highest priority — apply them for the whole session, including on resume. On any conflict with anything else in this skill — steps, gates, STOP rules — or with its Required Skills, standards documents, or operational learnings, the config line wins.

Workflow

Step 1: Establish Refactor Context

Start from the current pain, not from a preferred abstraction.

  • Identify the target area: a module, service, aggregate, endpoint path, or subsystem.
  • Clarify why the refactor is needed: mixed responsibilities, duplication, wrong-layer logic, coupling, poor testability, or unreadable control flow.
  • Clarify what the user expects to improve: simpler structure, correct layer placement, smaller units, clearer domain behavior, easier testing, or safer extension points.
  • Run framework:learning-harvest Load behavior. Focus hint: "refactoring session — focus: structural health, quality signals".
  • Run framework:context-anchoring Document Discovery to check for an existing context doc covering the affected feature/module:
    • Found → Load behavior. Honor every logged decision and constraint as an active commitment while planning the refactor. The doc already contains an approved refactor plan (preservation boundaries + target structure) → confirm it still matches the user's intent, then resume at Step 4 unless the user wants to revisit the plan first.
    • Not found → Proceed from the conversation and the current code. Do not block planning on a missing context doc.

End the step by summarizing the intent in one sentence:

"Refactor X to improve Y while preserving Z."

STOP: If you cannot state both the improvement target and the preservation target that clearly, continue clarifying with the user before planning any changes.

Persistence check:

  • Refactor is substantial, risky, or likely to span multiple sessions → ask whether the user wants to persist an approved plan.
  • A relevant context doc already exists and the user wants persistence → use it and update it.
  • No relevant doc exists and the user wants persistence → propose creating one; confirm the doc name per framework:context-anchoring, then use it as the source of truth for the approved plan.
  • The user declines persistence, or the refactor is small and local → continue in non-persistent mode. Approval gates still apply; the plan simply stays in-session.

Step 2: Define Preservation Boundaries

Refactoring changes structure, not behavior. Make the preservation contract explicit before proposing any structural edits.

List the behaviors that must remain unchanged:

  • Public API contracts and response shapes
  • Domain invariants and state transitions
  • Persistence semantics and side effects
  • Event emission and integration behavior
  • Authorization and security posture
  • Error behavior wherever externally visible
  • Performance or operational characteristics, if part of the current contract

Also list explicit out-of-scope changes:

  • New features
  • Schema changes
  • Contract changes
  • Intentional behavior changes
  • Unrelated cleanup outside the approved area

If the desired outcome requires changing preserved behavior, stop and discuss what the task actually is — a bug fix (/bug-fix), a feature change (/design-blueprint), or a broader redesign. STOP: Never proceed as a refactor after making one of those determinations.

Step 3: Propose High-Level Structural Plan

Zero Refactor Rule: make no structural code changes until the user approves both the target structure and the transition plan.

For small refactors the plan may be brief. For larger ones, use framework:design-first selectively:

  • Start at Level 2 (Components) to define target responsibilities and boundaries.
  • Use Level 3 (Interactions) when data flow or dependency direction will change.
  • Use Level 4 (Contracts) when internal interfaces or seams need to be formalized.
  • Skip Level 1 (Capabilities) unless the user-facing scope is actually changing.

Present:

  • Current structural problems -- what is wrong with the current shape
  • Target structure -- which components, classes, and functions should exist after the refactor
  • Movement plan -- what logic moves where
  • Preservation boundaries -- what will stay behaviorally unchanged
  • Out-of-scope items -- what will not be changed in this pass

End the step with an explicit gate:

"Does this refactor plan look correct? Should I proceed to Step 4: characterization tests?"

STOP: Do not write any refactoring code until the user explicitly approves this plan.

If persistence is enabled, use framework:context-anchoring Enrich to capture the approved preservation boundaries, target structure, movement plan, and out-of-scope items. STOP: When persistence is enabled, do not proceed to Step 4 until the approved plan is written.

Step 4: Add Characterization Protection First

Before changing any structure, lock in current behavior with tests.

  • Identify existing tests that already protect preserved behavior.
  • Strengthen existing tests that are too implementation-coupled or too vague to serve as guardrails.
  • Add characterization tests for important behaviors that are currently implicit.
  • Prefer the lowest-level test that faithfully captures each preserved behavior without missing important integration effects.
  • Characterization tests must describe the current observable behavior, not the intended refactored shape.
  • Apply framework:test-quality inline while writing them.
  • Run the safety net where the environment allows. If tests cannot be executed here, say so explicitly — the safety net then counts as unverified, and the stopping rule below applies.

Stopping rule:

  • An important preserved behavior is not protected by tests → pause and make that gap explicit before refactoring.
  • STOP: Do not start structural edits without a believable safety net unless the user explicitly accepts the risk.
  • Green characterization tests are the baseline for the refactor. Anything red before the first structural change must be resolved first, or the task re-scoped.

End the step with an explicit gate:

"Characterization tests in place and passing. Ready to discuss refactor strategy and pacing?"

STOP: Do not proceed to strategy selection until the safety net is verified green — or the user has explicitly accepted an unverified safety net per the stopping rule above.

Step 5: Choose Refactor Strategy and Pacing

With the high-level plan approved and the safety net green, choose the implementation approach.

Preferred strategies:

  • Extract and redirect -- extract focused units, route callers gradually.
  • Introduce seam, then migrate -- add an interface or boundary, then move behavior behind it.
  • Move behavior inward -- shift business rules from outer layers into the appropriate inner layer per framework:architecture.
  • Split and collapse -- separate unrelated responsibilities, then remove the old mixed path.

Ask the user to choose a pacing mode:

"How would you like to review the refactor?"

  1. Slice-by-slice (recommended) -- refactor one safe slice at a time, pausing after each slice. Best for risky legacy code.
  2. Layer-by-layer -- complete the refactor for one structural layer or concern, then pause for review. Best for broader architectural cleanup.
  3. Full autonomy -- execute the approved refactor end-to-end and present the complete result at the end. Best for tightly scoped, low-risk refactors. (Still pause immediately if any slice reveals the approved plan unsafe or invalid — see the Deviation Rule in Step 6.)

Default to slice-by-slice when the user expresses no preference.

Step 6: Refactor in Small Green Steps

Implement only within the approved preservation boundaries and the approved target structure.

For each slice:

  1. Make one structural improvement from the approved plan.
  2. Re-run the relevant characterization tests.
    • Any characterization test goes red → stop immediately. Do not start the next slice. Fix the regression or revert the slice before continuing.
  3. Apply the applicable atom Self-Validation Checklists.
  4. Run the applicable Active Anti-Pattern Scans.
  5. Fix violations before presenting the slice.
  6. Collect judgment calls for the slice via framework:collaborative-judgment and surface them before presenting the slice's code. Do not interrupt mid-slice unless the approved plan becomes unsafe or invalid.

Always apply:

  • framework:clean-code -- better boundaries, simpler control flow, smaller focused units, clearer naming.
  • framework:test-quality -- maintain strong characterization tests and nearby supporting tests.

Conditionally apply:

  • Responsibilities move across layers or dependency direction changes → apply framework:architecture.
  • Business rules, aggregates, value objects, or domain behavior move or sharpen → apply framework:domain-driven-design.
  • Trust boundaries, authorization, validation, queries, or sensitive data handling are touched → apply framework:secure-coding.

Deviation rule: if the implementation reveals the approved plan incomplete, unsafe, or requiring changes to preserved behavior → STOP: pause immediately and discuss before continuing.

Step 7: Verify Preservation and Structural Improvement

The refactor succeeds only if both are true:

  1. Behavior preserved
  2. Structure measurably better

Verify preservation:

  • Characterization tests still pass.
  • No unintended outward behavior changed.
  • Preserved contracts remain intact.
  • Security posture not weakened.

Verify structural improvement:

  • Responsibilities clearer.
  • Dependency direction improved, or at least no worse.
  • Duplication or entanglement reduced.
  • Testability and readability improved.
  • Old paths and temporary scaffolding removed once migration is complete.

When reporting completion, be explicit about both sides:

  • What behavior was preserved and how it was verified.
  • What structural improvement was achieved.
  • What was intentionally deferred to a later refactor.

Step 8: Capture Decisions and Remaining Debt

Use framework:context-anchoring Enrich to preserve the important parts of the refactor:

  • Refactor scope: what area changed.
  • Preservation boundaries: what was explicitly kept stable.
  • Target structure: what shape was approved.
  • Strategy chosen: why this migration path was selected over the alternatives.
  • Key files changed: path + role in the doc's Key Files table (skip a path already listed).
  • Deferred debt: what remains and why it was intentionally left for later.

No context doc exists and the refactor involved non-trivial structural reasoning → suggest creating one so the decisions are not lost across sessions.

Harvest learnings: run framework:learning-harvest Harvest behavior. Session context: "refactoring session — structural restructuring and debt resolution". Synthesize and propose cross-cutting patterns from this session — structural debt that accumulated, migration strategies that worked, characterization test gaps discovered. The user confirms what enters the document. STOP: run this before recommending /review below.

After the refactor is complete, recommend /review when the change:

  • touches multiple layers
  • changes domain boundaries
  • changes security-sensitive code
  • leaves temporary migration scaffolding behind
  • is large enough that an independent quality pass would add confidence