Kousigan A

AI coding agent scope creep: keep the fix, list the cleanup

Keep a coding agent to the task: state the scope and non-goals, back them with edit settings, spot unrelated edits and split the cleanup off.

Last verified against official docs on

To keep a coding agent from turning a small fix into a refactoring project, put the scope and the non-goals in the task. Name the files it may change, and ask it to list any cleanup it spots instead of doing it. Before you commit, read the diff for unrelated edits and split them off hunk by hunk with git add -p or git restore -p.

The tool behaviour below comes from each tool’s official docs and the Git docs, last verified on 11 October 2026. None of the agent behaviour was tested for this post. Two Git commands were run with Git 2.55.0 in a scratch repository on 11 October 2026: git diff HEAD -w --stat and git diff HEAD --name-status.

This is part 16 of the series on AI-assisted engineering. Part 11 covers reviewing the finished diff in risk order. This part is about keeping that diff small while the agent works.

A small change is easier to review and to revert

Small changes are old advice in code review, and a coding agent makes them matter more. An agent can edit many files in the time it takes you to read one. Google’s guide to small changes says small changes are “Reviewed more thoroughly” and “Simpler to roll back”. The same guide sets the right size as “a minimal change that addresses just one thing”.

Scope creep in an AI-generated change is any edit the task didn’t ask for. For example, a bug fix might arrive with a renamed helper, a reformatted file and an updated package.

Each extra might be fine on its own. Together they turn one decision into several, and the reviewer has to make all of them at once. And if you revert the commit later, the cleanup goes with the fix.

Sort every edit into one of two groups. A necessary edit is one the fix can’t work without, including its tests. An optional edit improves something the task never mentioned: names, formatting, structure or a newer dependency.

Google’s guide keeps the two apart: “It’s usually best to do refactorings in a separate CL from feature changes or bug fixes” (separate out refactorings). A CL is Google’s word for a change. The same section allows some slack for small cleanups, “such as fixing a local variable name”. Where the line falls is left to the developer and the reviewer.

Put the scope and the non-goals in the task

The task you hand the agent is the first place scope gets set. GitHub’s best practices for Copilot tasks ask for acceptance criteria. They also ask for “Directions about which files need to be changed.” Non-goals are the other half: what the agent should leave alone, even when it looks wrong.

Anthropic’s prompting guide has a sample instruction for keeping solutions minimal. One line of it reads: “A bug fix doesn’t need surrounding code cleaned up.”

A scoped task works better when it gives the agent somewhere to put its other ideas. An agent that spots a messy function can add it to a follow-up list and carry on with the fix. You keep the observation without the edit, and decide later whether it deserves its own change. Prompt 1 is a task with all of this filled in.

Back the file list with your agent’s edit controls

A file list in a prompt tells the agent where to work. It doesn’t stop an edit anywhere else.

Coding agents give you two more controls. One lets you see and reject edits before you keep them. The other asks before, or blocks, edits to certain paths. Checkpoints add a way to roll back a whole turn when the agent wanders off.

Agent See or reject edits before you keep them Roll back a turn Ask before, or block, edits to paths
Claude Code (permission modes, permissions, checkpointing) Manual mode (default) asks before file edits. acceptEdits edits without asking, for when you “want to review changes in your editor or via git diff after the fact”. From v2.1.283, interactive sessions start in auto mode, where “file edits in your working directory are auto-approved” /rewind, or Esc twice, then Restore code. “Checkpointing does not track files modified by Bash commands” Edit(...) allow, ask and deny rules with gitignore-style patterns, such as Edit(/src/**)
OpenAI Codex (permissions, CLI commands) /diff shows “the Git diff, including files Git isn’t tracking yet”, to “Review Codex’s edits before you commit or run tests” The CLI commands page lists no undo or rewind command The Ask for approval mode lets it “work within the current workspace” and pauses before reaching beyond it; change modes with /permissions
GitHub Copilot in VS Code (review and revert agent changes) In the Local harness, each edit is pending, and you Keep or Undo it, or “Hover over an inline change to accept or reject that specific change”. Agent Host sessions have no pending state, so you review the diff and Source Control Restore Checkpoint on an earlier request reverts it “and all later changes” chat.tools.edits.autoApprove takes glob patterns; files set to false show a diff and ask you to approve or reject the edit
Cursor (checkpoints, Agents window, Agent Review, ignore file) The Agents window has a diffs view to “review and commit changes”; /agent-review runs a code review on your local changes Restore a checkpoint from the chat timeline; “use Git for permanent version control” .cursorignore blocks Agent access to listed files, but “The terminal and MCP server tools used by Agent cannot block access” to them
Gemini CLI (configuration, checkpointing, ignore files) general.defaultApprovalMode: default “prompts for approval”, auto_edit “auto-approves edit tools” /restore, once checkpointing is turned on in settings.json; it is “disabled by default” .geminiignore excludes paths “from tools that support this feature”
Windsurf, documented as Devin Desktop (Cascade, modes, .devinignore) Permission modes in the mode selector “control how much the agent asks before acting” The revert arrow on a prompt reverts code to that step; the docs warn that reverts are currently irreversible Paths in .devinignore “cannot be viewed, edited, or created by Devin”; gitignored files “cannot be edited by the agent”

Each row comes from that tool’s official docs, last verified on 11 October 2026; none was tested for this post. Gemini CLI’s docs also note that for unpaid tier and Google One users, Gemini CLI was replaced by Antigravity CLI on 18 June 2026.

As a general practice, ignore files and deny rules fit paths that no task should change, like generated or vendored code. For a boundary that changes with every task, the prompt and the review controls fit better. Part 6 covers the same settings as a security boundary.

Check which mode your agent starts in. Claude Code’s auto mode approves file edits in the working directory. VS Code’s chat.editing.autoAcceptDelay accepts pending edits after a delay. With either, the diff is where you catch scope creep.

Spot unrelated edits in the diff

Unrelated edits come in a few familiar shapes, and most of them show in the file list before you read a line.

Unrelated edit How it shows A quick check
Formatting churn Many changed lines, little changed meaning Compare git diff HEAD --stat with git diff HEAD -w --stat
Renames and moves R in the file list for a staged rename; a plain mv shows as D plus a new untracked file git diff HEAD --name-status and git status
Drive-by refactors A changed function the task never mentioned Match each file against the task’s file list
Dependency bumps A changed manifest, such as package.json, or a lockfile The file list
Changed tests Tests edited or deleted outside the fix Test files the task didn’t name
New files Helpers, scripts or docs nobody asked for Untracked files in git status

The -w option tells Git to “Ignore whitespace when comparing lines” (git diff docs). In the scratch repository run on 11 October 2026, a file with whitespace-only changes dropped out of the second stat. The same run listed a staged rename as R100 and a plain mv as D. Other churn, like quote style or reordered imports, still needs a read.

An example set of commands for an uncommitted change:

bash
git status                       # changed files, and untracked new ones
git diff HEAD --stat             # lines changed per file
git diff HEAD --name-status      # A added, M modified, D deleted, R renamed
git diff HEAD -w --stat          # the same, ignoring whitespace changes

A changed test needs a closer look than other extras, because it can hide a weaker check; Part 11 covers how to read one. A new package is a decision of its own, and Part 12 is about those.

Keep the fix, and move the cleanup out

Optional cleanup goes in one of three places: a separate commit, a separate pull request, or the follow-up list. Git’s patch mode lets you split a file that holds both. A hunk is one block of changed lines in a diff.

git add -p stages part of a file. The git add docs say it lets you “Interactively choose hunks of patch between the index and the work tree”. For each hunk, y stages it, n skips it, s splits it into smaller hunks and e lets you edit it by hand.

An example split, with made-up commit messages:

bash
git add -p                    # stage the fix's hunks only
git diff --cached             # check exactly what will be committed
git commit -m "Fix rounding in invoice totals"
git add -p                    # now stage the cleanup
git commit -m "Rename helpers in the invoice module"

Each commit now holds one decision. You can open them as one pull request with two commits, or move the second to its own branch.

git restore -p goes the other way: it throws hunks away. The git restore docs describe it as: “Interactively select hunks in the difference between the restore source and the restore location.”

Two prompts: scope the task, then sort the diff

Neither prompt relies on a tool-specific feature, and neither lets the agent commit or discard anything. The splitting stays with you.

Prompt 1: a scoped change

An example prompt for a small fix, with the boundary and a place for the agent’s other ideas.

Prompt 1: a scoped change
Task: <describe the fix in one or two sentences>
Done when: <the behaviour or test that proves it>

Scope
- You may change: <files or folders>
- You may read whatever the task needs.
- Ask me before changing any other file.

Non-goals (leave these alone, even if they look wrong)
- No renames, reformatting or reordering of code the fix does not need.
- No dependency changes and no new packages.
- No edits to existing tests unless the fix changes the behaviour
  they check. Say which test and why.
- No new helpers, abstractions or configuration options.

If you notice something else worth fixing, do not change it.
Add it to a "Follow-ups" list at the end of your reply, with the
file, the line and one sentence on why.

Never print, expose or commit secret values.
Do not commit, push, deploy, run destructive commands or make external
changes unless I explicitly authorise it.
If two reasonable attempts fail, stop and report the failure rather
than repeatedly guessing.

End with every file you changed and one line on why the task
needed each change.

The follow-up list goes into your tracker or your next task, or nowhere.

Prompt 2: sort the diff into needed and optional

An example prompt for when the agent has finished and the diff looks bigger than the task.

Prompt 2: sort the diff into needed and optional
Task: sort the uncommitted changes in this repository against this
task: <paste the task>

Do not edit, stage, restore or delete anything.

For every changed or new file, and every hunk in it, mark one of:
- NEEDED: the task does not work without it, including its tests.
- OPTIONAL: a cleanup, rename, reformat or refactor the task did
  not ask for.
- UNRELATED: a dependency change, a test change or an edit outside
  the task.

Give the file, the line range and one sentence of evidence for each.
Flag any hunk that mixes needed and optional lines, so I can split it.
Mark anything you are not sure about as UNSURE rather than guessing.

Never print, expose or commit secret values.
Do not commit, push, deploy, run destructive commands or make external
changes unless I explicitly authorise it.
If two reasonable attempts fail, stop and report the failure rather
than repeatedly guessing.

End with a suggested split: what goes in this commit, what goes in a
separate commit or pull request, and what to drop.

Use its list as a guide while you run git add -p yourself.

A scope checklist before you commit

An example scope checklist follows the order of this post, ready to copy:

Scope checklist
SCOPE CHECK: AI-GENERATED CHANGE

□ Task names the files in scope and the non-goals
□ Every changed file is in scope, or explained
□ No formatting-only files (compare the stat with git diff -w)
□ No renames or moved files the task did not need
□ No manifest or lockfile changes the task did not need
□ No existing test changed without a reason tied to the task
□ No new helpers, scripts or config nobody asked for
□ Fix staged and committed on its own (git add -p)
□ Cleanup in its own commit, pull request or follow-up list

Quick answers

What is scope creep in AI-generated code?

Scope creep in AI-generated code is any edit the task did not ask for. Examples are a renamed function, a reformatted file, an upgraded package or a rewritten test. Each one adds a separate decision for the reviewer to make.

How do you stop an AI coding agent from changing unrelated files?

An AI coding agent stays closer to the task when the prompt names the files it may change and the non-goals. Edit approvals and deny rules add a check that doesn’t depend on the agent following instructions.

Should refactoring go in the same pull request as a bug fix?

Refactoring usually belongs in a separate commit or pull request from a bug fix. Google’s engineering practices say moving and renaming a class should be a different change from fixing a bug in it. Small cleanups, like fixing a local variable name, can stay with the fix.

How do you remove unwanted changes from a file before committing?

Git’s patch mode removes unwanted changes one hunk at a time. git add -p stages the hunks you choose. git restore -p discards the hunks you choose, and an unstaged edit it discards cannot be recovered.

How do you find formatting-only changes in a Git diff?

Formatting-only changes show up when you compare git diff --stat with the same command plus -w, which ignores whitespace when comparing lines. A file missing from the second list changed in whitespace alone. Quote style and import order still need a read.

More on AI coding is in the AI category.

Cartoon of Kousigan making a heart with his hands

Kousigan A

Build with AI. Ship with care.

Software engineer building real products with AI assistants. I write down what works: prompts, rules, skills and agents, and how to ship safely.