Giacomo Mantovani

Article · October 14, 2026 · By Giacomo Mantovani

The most common Claude Code mistakes (and how I removed them from my workflow)

Ten Claude Code mistakes I made or saw in my own repos, and the rule, hook or review step that now stops each one before it reaches main.

  • claude-code
  • ai-agents
  • developer-workflow
  • hooks
  • code-review
Don't trust "done". Review before merge. Rule, Script, Gate, Hook, Review.

I use Claude Code every day on client work and on this site. Most of what went wrong in the first months had nothing to do with the model being wrong about code. It came from the agent doing a reasonable thing in the wrong place: editing a checkout I was using, pushing to a branch that deploys, reading a file it should never open. Below are the ten mistakes I hit most often, and the specific mechanism that now prevents each one. Every fix is a line in a CLAUDE.md, a hook, or a review step that already runs in my repos.

Which Claude Code mistakes cost the most time?

The table is the short version. The sections after it explain each row with the actual rule or config.

Common Claude Code mistakes, their symptom and the fix
MistakeSymptomFix
Agent edits the main checkoutYour uncommitted work and the agent's changes mixRule: open a worktree before touching files
Orphaned worktreesStale directories and branches pile upLease worktrees from a tool and return them
Wait loop with pgrep -fThe loop never endsBackground the command or use a bracket pattern
No testsDone code that breaks on the next changeTDD rule: failing test first
Hidden type errorsBuild passes, types are brokenRun typecheck in the gate
Docs driftDocs describe code that no longer existsDocs = current state, map updated in the same diff
Agent reads secrets.env contents land in the transcriptPreToolUse hook that denies the command
Over-engineeringAbstractions nobody asked forYAGNI rules loaded every session
Unwanted PRs or pushesPRs opened or main pushed at the wrong timeExplicit delivery rules per repo
Trusting 'done'Bugs found after mergeReview agents and a gate before merge

Why should a Claude Code agent never edit the main checkout?

When Claude Code edits the directory you are working in, its changes and yours share one working tree. You cannot tell which edit is whose, a git stash takes both, and two agents in parallel overwrite each other. A git worktree fixes this: it is a separate checkout on its own branch that shares the repository history. The official worktree docs describe the --worktree flag and explain that a session isolated in a worktree has its edits and git commands blocked when they target the main checkout.

My global CLAUDE.md makes this the default: if the task modifies files in a repo, open a worktree first, and once inside it, read and edit only there. The rule is written as an ordered list, so the agent does not have to decide which mechanism to use.

How do orphaned worktrees happen, and how do you stop them?

Isolation creates its own mess. Per the Claude Code docs, a subagent worktree with changes stays on disk until a periodic sweep can remove it without losing work, and non-interactive -p runs have no exit prompt, so their worktrees are not cleaned up at all. After a few weeks of parallel agents I had directories under .claude/worktrees/ that nobody remembered creating.

The fix in my setup is to take worktree creation away from the agent's built-in tools. The global CLAUDE.md says never to use EnterWorktree or a subagent with isolation: "worktree", because those are the ones that left orphans for me. Instead the agent leases a worktree from a small CLI, works there with absolute paths, and returns the lease when it finishes. One lease per subagent. A worktree that is returned is cleaned; one that is still leased has an owner I can see.

path=$(treehouse get --lease --lease-holder claude)
# ... work inside "$path" ...
treehouse return "$path"

Why does an agent's wait loop with pgrep never finish?

Agents like to wait for a build or a server with a loop such as until ! pgrep -f "next build"; do sleep 1; done. The -f flag matches against the full command line, and the shell running that loop has "next build" in its own command line. The loop finds itself and waits forever, burning the session's time.

The rule I added is short: never wait on a process with pgrep -f inside a Bash command. Run the long command in the background and wait for the notification, use wait <pid>, or use a pattern that cannot match its own text. The bracket trick works because the regex [n]ext build matches "next build" while the literal string in the command reads "[n]ext build".

# loops forever: the shell matches itself
until ! pgrep -f "next build"; do sleep 1; done

# works: the pattern does not match its own command line
until ! pgrep -f "[n]ext build"; do sleep 1; done

How do you make Claude Code write tests every time?

An agent asked to add a feature will add the feature. Tests come later, or never, and the next change breaks the first one quietly. The rule in this site's CLAUDE.md is the first line under Rules: TDD is non-negotiable, write a failing test before the code, and no change is complete without a test. Two skills, tdd and testing, tell the agent which layer a test belongs in and how to run it with pnpm test.

Writing the test first also changes the conversation. If the agent cannot write a failing test, it usually does not understand the requirement yet, and that shows up before any code exists.

Why can a green build still hide type errors?

This site's Next.js config sets typescript.ignoreBuildErrors: true. That keeps deploys moving, and it also means pnpm build succeeds with broken types. An agent that checks only the build will report success. The repo says it in three places: CLAUDE.md ("type errors don't fail the build, run pnpm typecheck"), the workflow doc, and the ship config, where typecheck is listed as required because next build ignores type errors. Any project with a similar escape hatch needs the same explicit check, or the agent has no way to know the build is lying.

How do you keep docs from drifting when an agent changes code?

Agents are good at writing docs and bad at deleting them. Over time you get a README that describes three versions of the same feature. Two rules fixed this for me. First, docs describe the current state only: when behavior changes, update or delete the old text in the same diff, and leave history to git. Second, the repo has a code map, a set of short cards that cite path::symbol, and updating the affected cards is part of the diff, not a follow-up task.

During delivery, a dedicated docs agent runs after the code is final, reads what the diff actually changed, and updates the map and agent docs. It is the only agent in the pipeline with write access, so reviewers cannot drift the docs either.

How do you stop Claude Code from reading .env files?

A written rule ("only read .env.example") is in the workflow doc, but a rule is a request. A hook is enforcement. The hooks reference explains that a PreToolUse hook receives the tool input as JSON on stdin and can return a permissionDecision of deny with a reason, which stops the tool call.

My user settings register a PreToolUse hook on the Bash and PowerShell tools. It reads the command from tool_input, looks for any .env file name, allows only template names such as .env.example, .env.sample, .env.template and .env.dist, and denies everything else with a message pointing the agent to the example file. The output has this shape:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Shell access to .env.local blocked: use .env.example."
  }
}

The matcher covers shell commands only, so the written rule still matters for the other file tools. Layering both is the point: the rule explains why, the hook catches the case where the agent forgets.

How do you stop an AI agent from over-engineering?

Ask an agent for a cache and you may get a cache class, an interface, a config file and a factory. Each piece looks defensible; together they triple the diff you have to review. I load a set of lazy-developer rules into every session: check whether the thing needs to exist, reuse what is already in the codebase, prefer the standard library and native platform features, add no dependency for what a few lines can do, and write the smallest diff that works. Shortcuts with a known limit get a comment naming the limit, so they can be found later. The repo's coding standards add the same pressure from another side: reuse the existing UI primitives before writing new ones.

How do you stop Claude Code from opening PRs or pushing to main?

The defaults of most agent tooling assume a PR flow. This site has none: Vercel deploys main, and the delivery step merges a work branch directly. So the repo says it plainly in CLAUDE.md and again in the ship config: never open a PR, no gh pr command. Pushing is allowed only when the gate is green, after a rebase on origin/main. A merge conflict or a rejected push means stop and ask, and force-pushing main is never allowed. Global rules add that the agent commits or pushes only when asked.

The lesson for any team: write down the delivery path for each repo, including what the agent must not do. An agent cannot respect a convention it has never seen.

Why shouldn't you trust an agent when it says the task is done?

"Done" from an agent means it stopped. It does not mean the tests can fail, that nothing was weakened, or that the diff follows the standards. My /ship step runs review agents in parallel on every diff before merge: a generalist reviewer and a dedicated test reviewer that checks whether the tests are truthful, can actually fail, and were not modified or deleted without authorization. The coding standards are reviewed on every diff too. Lint, typecheck and tests form the gate. If anything blocking is still open after the fix rounds, nothing is merged; the agent reports the findings and a fix plan and asks.

This is the step that catches what the other nine fixes miss. It costs a few minutes per change, which is less than one bug found in production.

What are the common mistakes when adding these guardrails?

The first is writing rules without enforcement for anything risky. Secrets and deploys need hooks or permissions. The second is writing rules in vague terms ("be careful with git"). Each rule in my files names a command, a file or a flag. The third is putting everything in one giant prompt. Global rules live in the user CLAUDE.md, repo rules in the repo's CLAUDE.md, and the detail sits in linked docs and skills the agent opens when it needs them. The last is never revisiting the list. Every rule above exists because the agent got it wrong once.

Next step

Want this setup in your team's repos?

I help teams put AI coding agents to work with the rules, hooks and review steps that keep them out of production trouble.

Let's talk

For agent work beyond coding assistants, see my AI agent development service.

Frequently asked questions

Does a CLAUDE.md rule guarantee the agent follows it?

No. Claude Code loads CLAUDE.md as context, and the model usually follows it, but nothing enforces it. For actions that must never happen, such as reading secrets or running a destructive command, use a PreToolUse hook or a permission rule.

Are Claude Code worktrees cleaned up automatically?

Partly. According to the Claude Code docs, an interactive worktree session prompts you on exit, a clean subagent worktree is removed when the subagent finishes, and worktrees with changes wait for a periodic sweep. Worktrees from non-interactive -p runs are not cleaned up, so remove them with git worktree remove.

Can a hook block the Read tool as well as Bash?

Yes. A PreToolUse hook's matcher selects which tools it runs on, so you can register the same check for file tools. My hook covers shell commands; the written rule covers the rest.

Is TDD with an agent slower?

Each change takes a little longer to write. In exchange, the test exists before the code, the agent has a clear target, and later changes do not silently break earlier ones.

Author

Giacomo Mantovani

AI Engineer & Consultant for Companies and Agencies

LinkedInGitHub

Next step

Tell me the workflow. We scope it on a free call, then a working version is live within 10 days.

Book a scoping call

Related articles