Reformatted files it only opened to read
只是打开看看的文件,也被它格式化了
The agent reformats, re-wraps or restyles files orthogonal to the task, and the real change disappears in whitespace noise.
Applies to: Claude Code · Cursor · Codex · Copilot agent
Symptom
Hundreds of changed lines, almost none of them functional. Quote styles, trailing commas or import order changed in files the task never needed.
Why it happens
Agents "run formatters or tidy up while they're there" (agent-review AR004). claudecodeguides.com calls it the reformatting impulse, cleaning up files Claude opens only to check an import.
Example
From the agent-review taxonomy (AR004).
diff- const x = 1; const y = 2; const z = 3
+ const x = 1
+ const y = 2
+ const z = 3
Copy-paste fixes
Pick the files for the agents you use. Snippets are in English so they work in any repo.
Claude Code: add to CLAUDE.md
CLAUDE.md## No drive-by formatting
- Do not reformat, re-indent, reorder imports or change quote style in code you are not functionally changing.
- Run the formatter only on the lines or files you changed (for example `npx prettier --write <changed files>`),
never on the whole repo, unless asked.
- If a file needs a format pass, propose it as a separate commit.
Codex, Copilot coding agent and others: add to AGENTS.md
AGENTS.md## No drive-by formatting
- Do not reformat, re-indent, reorder imports or change quote style in code you are not functionally changing.
- Run the formatter only on the lines or files you changed (for example `npx prettier --write <changed files>`),
never on the whole repo, unless asked.
- If a file needs a format pass, propose it as a separate commit.
Cursor: project rule
.cursor/rules/drive-by-reformat.mdc---
description: "AgentFails: Reformatted files it only opened to read"
alwaysApply: true
---
## No drive-by formatting
- Do not reformat, re-indent, reorder imports or change quote style in code you are not functionally changing.
- Run the formatter only on the lines or files you changed (for example `npx prettier --write <changed files>`),
never on the whole repo, unless asked.
- If a file needs a format pass, propose it as a separate commit.
Format only changed files (pre-commit)
lint-staged runs the formatter on staged files only, so a session can't reformat the repo through the commit hook.
package.json{
"lint-staged": {
"*.{ts,tsx,js,jsx,css,md}": "prettier --write"
}
}
Diff budget check (run before commit or in CI)
Adapted from the diff-budget post on dev.to (yureki_lab). Reads the real git diff, so it also catches edits made through the shell. Usage: python3 scripts/diff_budget.py origin/main 6 150
scripts/diff_budget.py#!/usr/bin/env python3
"""Fail if the diff vs BASE touches files outside .agent-scope or exceeds the budget."""
import fnmatch, os, subprocess, sys
base = sys.argv[1] if len(sys.argv) > 1 else "HEAD"
max_files = int(sys.argv[2]) if len(sys.argv) > 2 else 6
max_lines = int(sys.argv[3]) if len(sys.argv) > 3 else 150
allowed = []
if os.path.exists(".agent-scope"):
allowed = [l.strip() for l in open(".agent-scope") if l.strip() and not l.startswith("#")]
out = subprocess.run(["git", "diff", "--numstat", base], capture_output=True, text=True, check=True).stdout
untracked = subprocess.run(["git", "ls-files", "--others", "--exclude-standard"],
capture_output=True, text=True, check=True).stdout.split()
files, lines, outside = 0, 0, []
rows = [r.split("\t") for r in out.strip().splitlines()] + [["0", "0", p] for p in untracked]
for added, deleted, path in rows:
files += 1
if added != "-":
lines += int(added) + int(deleted)
if allowed and not any(fnmatch.fnmatch(path, p) for p in allowed):
outside.append(path)
problems = []
if outside:
problems.append("out-of-scope files: " + ", ".join(outside))
if files > max_files:
problems.append(f"{files} files changed (budget {max_files})")
if lines > max_lines:
problems.append(f"{lines} lines changed (budget {max_lines})")
if problems:
print("OVER BUDGET: " + "; ".join(problems))
sys.exit(1)
print(f"within budget: {files} files, {lines} lines")