"Login is broken." Claude Code changes something and login works, but signup now throws. It fixes signup, and the session cookie stops being set. Forty minutes later there are eleven modified files, none of the original error message is left anywhere, and you can no longer say which change was the one that helped.

This is the most expensive failure mode in AI-assisted work, and it starts with a symptom handed over without a way to reproduce it.

The idea

An agent given a symptom and no reproduction will guess, and a guess that half works is worse than one that fails outright: it changes the error, which looks like progress. The way out is to make each attempt falsifiable and cheap to undo. Reproduce the failure yourself, shrink it, read the actual error text, change one thing, and check that one thing.

text
  the circle                          the line
  ----------                          --------
  report the symptom                  reproduce it yourself, on demand
        |                                     |
  agent guesses a fix                 shrink it to the smallest failing case
        |                                     |
  a different error appears           read the real error, top line and file path
        |                                     |
  paste the new error ---+            change exactly one thing
        ^                |                    |
        +----------------+            did that one thing fix it?
                                          |            |
                                        yes           no
                                          |            |
                                       commit    revert to the last
                                                 good commit and restate

How it works

Reproduce before you ask. A failure you cannot trigger on demand cannot be verified as fixed, so every "fixed it" that follows is unfalsifiable. The reproduction is a recipe: the exact steps, the exact input, the exact error text.

Read the real error rather than the summary of it. What you want out of a is the line naming the error type and its message, and then the frame nearest to it naming a file you wrote. Which end to read from varies: Node prints the error near the top with the innermost call under it, while Python prints frames oldest first under a Traceback (most recent call last): header, so its error line is last and the frame that broke sits just above. Rather than guess, paste the whole thing. An agent handed "it errors" invents a cause; an agent handed the trace reads one.

One change at a time. When three edits ship together and the symptom moves, you have learned nothing about which edit did it. Ask for the cause before the fix: "explain why this happens, and do not change anything yet" costs one turn and saves the guess.

Anthropic's Claude Code guidance sets the stopping rule: after two failed corrections, start fresh with a better prompt built from what you learned. By then the is full of failed approaches, and the model keeps re-reading its own wrong turns as if they were context.

Reverting is the part people skip. Version control is what makes a bad direction disposable instead of something to defend. Version control, plainly covers the mechanics.

What to do

  1. Snapshot before you start, and write the commit's id down. git add -A is the part that matters: git commit -am stages edits to files git already tracks and silently skips every file the agent newly created, which here is most of them. Glance at git status first, because -A will just as happily commit a .env the agent wrote.

    bash
    git status                  # look before staging everything
    git add -A
    git commit -m "before login fix"
    git log --oneline -1        # note the id, e.g. 9f2c1ab
  2. Write the reproduction into the request: steps, input, and the error pasted in full. Ask for the cause first and the fix second.

  3. After the second failed fix, go back to that id. Name the id itself, not HEAD: HEAD is shorthand for whatever the latest commit happens to be, and by now that is often one the agent made rather than yours. Run this from the project root, because the last command only reaches downwards.

    bash
    git reset --hard 9f2c1ab    # branch moves back. Commits made after 9f2c1ab
                                # are dropped, tracked files are overwritten, and
                                # tracked files that did not exist then are deleted
    git clean -nd               # dry run. -d is required or new FOLDERS are not listed
    git clean -fd               # actually delete those untracked leftovers

    Order matters: the reset changes which files are untracked, so a dry run done beforehand describes a tree that no longer exists. Uncommitted edits and cleaned files are gone for good. Dropped commits are not, and git reflog still lists them if you reset to the wrong id.

  4. Start a fresh session and describe the bug plus everything you ruled out, rather than continuing the conversation that accumulated the failures.

Where it breaks

Some bugs will not reproduce on your machine. Timing bugs and bugs that need real traffic or the production database are common, and the answer there is better evidence rather than more attempts: logs from the moment it failed, the actual request, the actual data.

Reverting also destroys good work when you have not been committing. What a revert costs is set entirely by how long ago your last commit was.

And a clean reproduction can still point at the wrong layer. When the smallest failing case involves a library you did not write, check its version and its release notes before assuming your own code is at fault.