claude-code

Refine

The refinement toolkit - what each verb takes as its object here, and the test it has to pass:

Throughout, <repo> is the project root.

Procedure

1. Establish scope

Resolve what is in and out of scope. Bias towards recent work and towards the code that would benefit most, and narrow without asking - one intercorrelated subset refined well beats the whole repository refined mediocrely. Take the module, layer, or dependency cluster you can carry through every step below with room to spare.

In scope

Whatever the user named, or is closely related to it. Expect a branch diff, commits, a commit range, staged or unstaged changes, or the name of a module, component, or code unit. A named scope too large to refine well is still narrowed; absent any input, choose the subset yourself. Either way, Scope records what you deliberately left for a later pass.

Earlier passes left their reports in .agent/outbox/refine/. Read their Scope sections before settling on yours, and where the obvious target is one of them, take a sibling or the older code beneath it instead - a slice nobody has picked over yields more per token than a second look at one already refined.

Always out of scope

Adding features, implementing plans, and consequential changes to external-facing product behavior.

Output file

Every pass writes a report to .agent/outbox/refine/<sha>.md, where <sha> is git rev-parse --short HEAD, suffixed -dirty when git status --porcelain is non-empty. A second pass at the same sha overwrites the file.

It has five sections, filled in as the pass proceeds: Scope, Fixed, Changed, Needs your review, and Baseline. Needs your review is a numbered list, never bulleted, so the user can answer by number. Write the resolved scope now.

Validation the user has to run

If validating this scope needs a person - a manual runtime exercise, credentials, hardware - ask once with AskUserQuestion before the first edit. Absent an answer, run the automated subset and name the uncovered surface in the report. Nothing after this step blocks on the user.

2. Load guidance

3. Establish the validation baseline

Perform as much of the following as the resolved scope warrants and the project supports:

Record into the Baseline section of the output file:

In the file, not in context: Step 7 reads it back after a long pass, and pre-existing failures have to stay distinguishable from yours.

4. Close the gaps in validation coverage

Within the resolved scope, search for what no check currently defends:

Write checks that would expose a regression in each.

Bound this to the code you expect Step 6 to touch. Coverage here is the net under planned changes, not an end of its own, and a scope can absorb more test-writing than the refinement it exists to protect. An uncovered gap outside that set is named under Needs your review and left alone.

A new check can also expose behavior that looks wrong. Do not assert it - a test asserting a bug is a bug with tenure. Carry it into Step 6 as a bug.

5. Re-establish the baseline

Repeat Step 3 with the added coverage, iterating until every new check passes, and replace the Baseline section with the result. This is the baseline refinement runs against.

6. Refine the implementation

Review the code for constructive changes, using the guidance from Step 2 as inspiration. Be zealous - the baseline is what makes a large restructuring cheaper to attempt than to agonize over.

A pass that finds nothing goes looking rather than settling. Where the slice turns out to be already refined, do not manufacture churn to justify the run and do not stop either - return to Step 1 once and take the nearest scope that is not already clean: a sibling module, the layer beneath, the older code the recent work was built on. Steps 3 through 5 run again for the new scope, and the tokens that bought the first survey are not wasted on the second. Scope records the slice you found clean alongside the one you redirected to.

Report an empty Changed only when the redirect also comes back empty.

Bugs

Hunt these first and weight them above everything else here. One defect removed is worth more than any number of files made tidier, so a pass that reports only refactors is a pass that did not look hard enough. Go after the crash, the unhandled failure, the off-by-one, the contract the code violates, the case the docstring promises and the implementation does not deliver.

Fix it outright where the correct behavior is not in question - the code contradicts its own documented contract, or no reasonable reading of this project wants what it currently does. Cover each fix with a check that fails before it and passes after, and record it under Fixed: the defect, what triggers it, and the corrected behavior.

Where the right behavior is a product decision, where something may already depend on the defect, or where the fix is consequential and external-facing, raise it instead of fixing it. Never sit on one silently.

Clarity

After bugs, this is what the pass is for, and it outranks tidiness of every other kind. Go after:

Choose clarity over brevity: explicit code that reads in one pass beats compact code that does not. A shorter line that costs the reader a second pass is not a refinement.

Each of these still has to clear one of the two bars below.

Uncontestable changes

For everything that is not a bug fix, the bar is binary. A change belongs to this pass only if it leaves behavior, output, and every public contract observably identical. Make it, then write it to Changed, grouping same-class changes onto one line.

Renames, extractions, and moves are the common case to rule on, and the boundary decides them: one confined to a module, where every caller is inside the scope you are already changing, clears the bar and belongs here. One that alters what a module, package, or public API exports does not, however much clarity it would buy - that is a contestable change.

Contestable changes

Anything that fails that bar is worth raising rather than discarding, however good it is: the bugs held back above, unoptimized implementations, undesirable product behavior, and refactors of load-bearing code too risky for a pass with no one watching.

Number each entry under Needs your review, and give the proposed change, what refinement would gain from it, and the functional impact that kept it out.

7. Verify

Re-run the Step 5 commands and compare against the Baseline section. Every signal must come back equal or better.

Some deltas are the point rather than a regression - a bug fix flips a check that encoded the defect, deleted dead code takes its tests with it, an extracted function splits one test into several. Where a delta is intended, keep the change and record it in Fixed or Changed with its justification. Where it is not, fix it or back the change out.

8. Correct documentation, prune comments

Reconcile every document Step 2 flagged as suspect, plus anything this pass invalidated - a renamed symbol, a moved path, a changed command - whether or not that document was itself in the resolved scope.

Documentation follows the code’s vocabulary, not its own: where the two name a thing differently the document is corrected, even when its word is the better one - fix the code first if it is, then bring the document to it.

A document describing implementation as it no longer is gets corrected to match, not deleted: stale documentation is a defect, and it is fixed like one. Verify the correction rather than assuming it - run the command, follow the path. Delete instead of correcting only where the content should not exist at all, because it violates english.md by restating code, narrating the journey, or duplicating a fact that lives elsewhere. Record both under Changed.

Then sweep every comment within the resolved scope. Here deletion is the default and rewriting the fallback: on a first sweep of a scope, expect to delete far more than you write, and treat a comment count that barely moved as evidence the rule went unapplied. Where a previous pass already swept this code, that expectation is spent - the survivors earned their place, and deleting them to hit a quota is the failure mode. The rule: a comment survives only by stating a constraint the code cannot show - a reason, an invariant, a caveat, a pointer to why. Delete on sight:

Where a comment exists because the code beneath it is unclear, fix the code and delete the comment.

Where this touches code files, re-run the relevant formatting, linting, and validation.

Finally, run the english.md audit pass over every artifact this session wrote, the output file included.

9. Report done

Name the output file by its path relative to <repo>, and give the highlights in no more than a couple of sentences.