Skip to content

A CLAUDE.md that keeps agents on the rails

The conventions, guardrails, and MCP setup I use to run long autonomous coding sessions without drift.

Claude Code DX

I run most of my build work through long, autonomous Claude Code sessions — the kind where I describe a feature, step away, and come back to a branch. The difference between a session that ships something I’d actually merge and one that quietly drifts into its own private idea of the project almost always traces back to a single file: CLAUDE.md.

It’s the least glamorous file in the repo and the one I spend the most time tuning. Here’s what I’ve learned putting one in front of every project I hand to an agent.

What the file is actually for

There’s no magic in CLAUDE.md. It isn’t a config format the tool parses for behavior — it’s a plain markdown file read into the model’s context at the start of a session, the same way you’d brief a contractor before handing them the keys. That’s the whole mechanism, and it tells you exactly what to put in: the things a sharp engineer who’s never seen your repo would need before touching it, and would otherwise reconstruct from scratch — slowly, and sometimes wrong.

That reconstruction is the failure mode it prevents. With no brief, an agent infers your conventions from whatever files it happens to open first. Sometimes it guesses right. Sometimes it sees one old module using require() and decides that’s the house style, drops a new file in a folder that fights your layout, or “helpfully” reformats something you never asked it to touch. A good CLAUDE.md replaces the guessing with a stated brief: here’s the stack, here’s how we do things, here’s what not to touch.

The mental model I keep is a standing brief rather than documentation. Docs explain the system to a human reading them once; the brief tells the agent how to behave every time, in this repo, today. Mine tend to open with a literal instruction to that effect — the philipdamra.dev one starts, “Read this first. It’s the standing brief.”

A few mechanical facts shape how I use it. The project file lives at the repo root as ./CLAUDE.md and loads automatically; Claude Code also reads CLAUDE.md files up the directory tree, so a parent folder’s file applies to everything beneath it. There’s a user-level file at ~/.claude/CLAUDE.md that rides along in every project — mine holds what’s true of all my work (TypeScript, AWS CDK, never use any) rather than anything repo-specific. And CLAUDE.md files in subdirectories get pulled in on demand when the agent reads files in that part of the tree, which is how you give a big monorepo local context without bloating the root file.

In context from the first turn

on demand

enterprise / managed policy

~/.claude/CLAUDE.md · your conventions, every project

./CLAUDE.md · this repo (and parent dirs)

packages/x/CLAUDE.md · loaded when the agent works there

the agent's working context

The split between the user file and the project file matters more than it looks. The user file is your engineering identity — the conventions you carry everywhere. The project file is everything true here and nowhere else. Keeping them separate is what keeps the project file short, and short is the whole game: every line you add competes with the actual task for the model’s attention. A bloated brief doesn’t make the agent more careful — it makes it skim. The official guidance puts the ceiling around 200 lines; mine usually run well under that.

What belongs in it

After enough of these, the same small set of sections does the real work. None of them are long.

The stack and the versions. Two lines — runtime, framework, major versions. It stops the agent reaching for an idiom from the wrong era: the React 19 answer when you’re on 18, the v2 API when you’ve pinned v1.

A repo map. A small tree with one-line notes on what lives where. This earns its space faster than anything else in the file: it saves the agent a dozen exploratory greps, and — more to the point — stops it from putting a new file in the wrong place.

The commands. Build, test, lint, deploy — the exact incantations, because the agent will run them, and a guessed npm test that doesn’t exist burns a turn and a little trust. Mine list npm run build, npm run synth, and npm run deploy, with a note on which one needs AWS credentials.

The house style, stated rather than implied. This is where the agent most wants to improvise. My global file is blunt about it: never use the any type, no require() imports, no DynamoDB scans, no magic numbers, prefer arrow functions. Phrased as rules, not preferences — because the model weighs them differently. “We tend to avoid scans” gets considered; “No DynamoDB scans — always query with a proper access pattern” gets followed.

The explicit don’ts. Every repo has a few things that look fixable but aren’t: the generated directory you must not hand-edit, the region you can’t move, the ugly workaround holding something together. The realysis repo frames its entire docs philosophy this way — the guides exist to capture “the footguns we’ve already stepped on,” so the next person, human or agent, doesn’t step on them again. CLAUDE.md is the cheapest place to write one down the first time it bites you.

How to know it’s done. The last section of mine is always a verification rule, because “looks done” and “is done” are different claims and an agent will happily make the first one. Mine states that the build must pass before any task is called complete, and that infra changes must pass a synth too. One sentence, and it changes the agent’s exit criteria.

A note on getting started: /init will analyze a repo and write a first CLAUDE.md for you, which is a fine way to begin — but treat its output as a rough draft, not the file. The version that actually keeps an agent on the rails is the one you’ve cut in half and sharpened against a few real sessions.

The three MCP servers I keep

A clarification first, because it trips people up: CLAUDE.md doesn’t configure MCP servers — that lives in .mcp.json or your Claude settings. But the brief is the right place to note which servers are available and when to reach for them. Across a lot of sessions I’ve converged on a deliberately small set. Three.

Tailwind Plus, for frontend work. It gives the agent real, vetted component patterns to build from, so the UI it produces looks designed rather than defaulted. Left to its own instincts, an agent’s idea of an interface is a sort of universal gray-and-blue dashboard; with a component source to draw on, it builds in your actual system.

An AWS MCP server, for everything cloud. The value here isn’t automation, it’s grounding. AWS’s surface area is enormous and shifts constantly, and a model’s memory of an API or a service quota is exactly as current as its training cutoff. Pointing it at live AWS documentation means it checks instead of guessing — which, on AWS, is the line between code that deploys and code that looked plausible eighteen months ago.

Playwright, for proof. This is the one I’d give up last. It lets the agent drive a real browser — open the app it just changed, click through a flow, screenshot the result, read the console. It turns “I added the feature” into “here’s the feature working,” which is the whole difference between an agent that claims success and one that can show it. Most of the verification rules in my CLAUDE.md quietly resolve to “and then prove it in Playwright.”

The discipline is the smallness. Every server you add puts more tools in the agent’s context and more ways for it to wander; three well-chosen ones cover design, cloud, and proof without turning each session into a tool-selection problem.

Catching drift before it compounds

Even with a good brief, long sessions drift. The model is taking hundreds of small reasoning steps, and a wrong assumption early — a misread of how a function is called, a feature it believes exists — compounds quietly until it’s three files deep in a fix for a problem you don’t have. The brief reduces drift. It doesn’t abolish it. The rest is operating discipline.

Scope to something checkable. The biggest single lever is the size of the task you hand off. “Refactor the auth module” invites drift; “extract the token-refresh logic into its own function and add a test” doesn’t, because it has an edge — a point where it’s unambiguously done. A CLAUDE.md that defines how work gets verified makes each task self-checking, which is really just giving the agent its own off-ramp.

Watch for the tell. Drift has a signature. The agent starts explaining more and showing less; it opens a fix with “the issue might be”; it edits a third file to make the first two work. When it’s reaching rather than knowing, I stop.

Reset rather than nudge. The instinct when an agent goes wrong is to correct it in place — “no, not like that, do this.” It usually fails, because the bad context is still sitting there shaping everything after it. Once a session has genuinely wandered, the move is to reset: clear it and start the next attempt from a clean context, with a sharper prompt and, often, one more line in CLAUDE.md so the same wrong turn isn’t on the menu next time. When a correction is worth keeping, you can tell the agent to add it to CLAUDE.md directly, or drop it in yourself through /memory. And recent versions keep their own running notes on the corrections you repeat, so some of this upkeep now happens on its own. That’s the loop that matters: most drift you can trace to a missing instruction is one line away from never happening again.

The shape of a good one

A good CLAUDE.md is short, specific, and a little opinionated. It names the stack, maps the repo, lists the commands, states the rules, and says how to know the work is done — and then stops, because every line past the necessary ones is a line the agent has to wade through to reach the task. It’s a living file: I add to it the first time an agent surprises me, and I cut from it whenever a line stops being true.

Mine are never finished, which is the point. The brief is how a project tells the agent who it is at the start of every session — and the projects that ship cleanly through an agent are, almost without exception, the ones whose brief I’ve bothered to keep sharp.