From harness-claude
Guides safe refactoring with test suite and harness verification at every step, preserving behavior while improving structure, readability, and maintainability.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Safe refactoring with constraint verification at every step. Change structure without changing behavior, with harness checks as your safety net.
Guides refactoring code structure and modules to improve maintainability, readability, and performance while preserving external behavior and ensuring all tests pass.
Orchestrates safe refactoring ensuring tests pass before/after changes. Requires explicit approval for test modifications and generates characterization tests for low-coverage code.
Safely refactors code test-first: verifies/writes tests, one structural change at a time, preserves behavior. Use for 'refactor this', 'clean this up', reorganization.
Share bugs, ideas, or general feedback.
Safe refactoring with constraint verification at every step. Change structure without changing behavior, with harness checks as your safety net.
All tests must pass BEFORE you start refactoring and AFTER every single change.
If tests are not green before you start, you are not refactoring — you are debugging. Fix the tests first. If tests break during refactoring, undo the last change immediately. Do not try to fix forward.
Run the full test suite. Every test must pass. Record the count of passing tests — this number must not decrease at any point.
Run harness validate and harness check-deps. Both must pass. You are establishing a clean baseline. If either reports issues, fix those first (that is a separate task, not part of this refactoring).
Run compute_blast_radius on target files to understand downstream impact before refactoring. This reveals which modules, tests, and consumers will be affected by structural changes.
Identify the refactoring target. Be specific: which file, function, class, or module? What is wrong with the current structure? What will be better after refactoring?
Plan the steps. Break the refactoring into the smallest possible individual changes. Each step should be independently committable and verifiable. If you cannot describe a step in one sentence, it is too large.
When a knowledge graph exists at .harness/graph/, use graph queries for faster, more accurate context:
get_impact — precise impact analysis: "if I move this function, what breaks?"query_graph — find all transitive consumers, not just direct importersCatches indirect consumers that grep misses. Fall back to file-based commands if no graph is available.
When you encounter an unknown during refactoring, classify it immediately:
Do not guess whether a change is behavioral or structural. If you are unsure, it is blocking.
For EACH step in the plan:
Make ONE small change. Examples of "one small change":
Run the full test suite. All tests must pass. If any test fails:
Run harness validate and harness check-deps. Both must pass. A refactoring that fixes code structure but violates architectural constraints is not safe.
Commit the step. Each step gets its own commit. The commit message describes the structural change: "extract validateInput from processOrder" or "move UserRepository to data-access layer."
Repeat for the next step in the plan.
Run the full test suite one final time. Same number of passing tests as Phase 1.
Run harness validate and harness check-deps one final time. Clean output.
Run detect_stale_constraints to verify architectural constraints are still valid after structural changes. Refactoring can render existing constraints obsolete or misaligned.
Run check_traceability to confirm refactoring didn't break requirement-to-implementation mappings. Moved or renamed code may orphan traceability links.
If a knowledge graph exists at .harness/graph/, refresh it after code changes to keep graph queries accurate:
harness scan [path]
Skipping this step means subsequent graph queries (impact analysis, dependency health, test advisor) may return stale results.
Review the cumulative diff. Does the final state match the intended improvement? Is the code genuinely better, or just different?
If the refactoring introduced no improvement, revert the entire sequence. Refactoring for its own sake is churn.
When: A function is doing too many things, or a block of code is reused in multiple places.
How: Identify the block. Ensure all variables it uses are either parameters or local. Cut the block into a new function with a descriptive name. Replace the original block with a call to the new function.
Harness guidance: If the extracted function belongs in a different layer, move it there AND update the import. Run harness check-deps to verify the new import respects layer boundaries.
When: Code is in the wrong architectural layer (e.g., business logic in a UI component, database queries in a service).
How: Create the function in the correct layer. Update all callers to import from the new location. Delete the old function. Run harness check-deps after each step.
Harness guidance: This is where harness check-deps is most valuable. Moving code between layers changes the dependency graph. The tool will tell you immediately if the move created a violation.
When: A file has grown too large or contains unrelated responsibilities.
How: Identify the cohesive groups within the file. Create new files, one per group. Move functions/classes to their new files. Update the original file to re-export from the new files (for backward compatibility) or update all callers.
Harness guidance: Run harness validate after splitting to ensure the new files follow naming conventions and are properly structured. Run harness check-deps to verify no new boundary violations.
When: An abstraction (class, interface, wrapper function) adds complexity without value. It has only one implementation, is never extended, and obscures what the code actually does.
How: Replace uses of the abstraction with the concrete implementation. Delete the abstraction. Run tests.
Harness guidance: Removing an abstraction may expose a layer violation that the abstraction was hiding. Run harness check-deps to check.
When: A name is misleading, ambiguous, or no longer reflects what the code does.
How: Use your editor's rename/refactor tool to change the name everywhere it appears. If the name is part of a public API, check for external consumers first.
Harness guidance: Run harness check-docs after renaming to detect documentation that still uses the old name. AGENTS.md, inline comments, and doc pages may all need updating.
harness validate — Run before starting, after each step, and at the end. Catches structural issues, naming violations, and configuration drift.harness check-deps — Run after each step, especially when moving code between files or layers. Catches dependency violations introduced by structural changes.harness check-docs — Run after renaming or moving public APIs. Catches documentation that references old names or locations.harness cleanup — Run after completing a refactoring sequence. Detects dead code that the refactoring may have created (unused exports, orphaned files).compute_blast_radius — Run in Phase 1 PREPARE before making changes. Reveals downstream impact of target files so you understand what the refactoring will affect.detect_stale_constraints — Run in Phase 3 VERIFY after refactoring. Checks whether architectural constraints are still valid after structural changes.check_traceability — Run in Phase 3 VERIFY after refactoring. Confirms requirement-to-implementation mappings were not broken by moved or renamed code.harness validate passes at every stepharness check-deps passes at every stepharness cleanup to verify)| Flag | Corrective Action |
|---|---|
| "This refactoring is safe, I don't need to run tests after this small change" | STOP. The Iron Law is absolute: tests after EVERY change. "Small" and "safe" are the changes that introduce subtle bugs. |
| "I'll combine these two renames into one commit since they're related" | STOP. One change per commit. Combined changes make it impossible to isolate which change caused a regression. |
| "The failing test is testing implementation details, so I'll fix the test" | STOP. Changing tests during refactoring is a warning sign. Verify the test is actually testing implementation details — not behavior your refactoring inadvertently changed. |
// removed old implementation or // TODO: move back later replacing functional code | STOP. Either the code lives in its new location or it doesn't. Comments are not migration plans. Keep the code or delete it with a test proving the deletion is safe. |
| Rationalization | Reality |
|---|---|
| "The tests are mostly passing, so I can start refactoring and fix the remaining failures as I go" | All tests must pass BEFORE refactoring starts. If tests are not green before you start, you are not refactoring -- you are debugging. |
| "This refactoring changes a small amount of behavior, but it is a clear improvement" | Refactoring must not change behavior. The test suite is the proof. If the refactoring requires changing tests, you may be changing behavior. |
| "I will make several changes at once and run tests at the end since each change is small" | Tests must run after EVERY single change. If a test breaks, you must undo the LAST change immediately. |
| "The refactoring did not produce a measurable improvement, but the code is different so it must be somewhat better" | If the refactoring introduced no measurable improvement, revert the entire sequence. Refactoring for its own sake is churn. |
| "I will refactor this and add the new feature in the same pass to be efficient" | Refactoring and feature work are separate tasks. Mixing them means test failures could be from the refactoring OR the new behavior — you cannot tell which. |
| "The test suite is slow, so I will run tests only at the end of the refactoring sequence" | Each step must be independently verified. A slow test suite is a separate problem to solve — it is not a reason to skip the safety net. |
Target: src/components/OrderSummary.tsx contains a calculateDiscount() function with complex business rules. This logic belongs in the service layer.
Step 1: Create src/services/discount-service.ts with the calculateDiscount function copied from the component.
harness check-deps: pass (new file, no violations)Step 2: Update OrderSummary.tsx to import calculateDiscount from discount-service instead of using the local function.
harness check-deps: pass (UI importing from service is allowed)Step 3: Delete the original calculateDiscount function from OrderSummary.tsx.
harness check-deps: passharness cleanup: no dead code detectedFinal verification: 3 steps, 3 commits, all tests green throughout, all harness checks passing. The business logic is now in the correct layer.
harness check-deps fails after a move: The code you moved may have dependencies that are not allowed in its new layer. You may need to refactor the moved code itself (remove forbidden imports) before it can live in the new layer.