GnothiGnothi
SeriesFieldsCommunityD&DPublishing
Sign inGet started free

Writing the Instructions Your Next Session Will Actually Read

The founding charter

The core of this chapter is CLAUDE.md, the file Claude Code loads automatically at the start of every session before your first request arrives — no attaching, no pasting, just a file sitting at the project root or inside the .claude directory that gets read on its own. It walks through the four places instructions can live — the shared project file, a personal file in your home directory, an untracked local variant, and organization-wide settings — and explains that Claude Code doesn't let the narrowest file override the others; it concatenates all of them into context, broadest first, and leans on the model to treat the more specific instruction as the one that wins.

From there the chapter builds an actual file for a shared-list app: starting from a /init draft, keeping only the commands that aren't obvious (the dev server, the type check, the one authoritative test command), naming the manual click-through journey that tests alone can't replace, writing down two or three real architectural constraints, and stating flatly what "finished" means. It explains why the file should stay short — it's re-injected on every turn, so bloat taxes every future question — and points to /context, /doctor, and /memory as ways to check what's loaded and trim what doesn't earn its place. It draws a hard line between guidance, which the model can still talk itself past, and real enforcement through a permissions deny list, and closes with a repeatable loop for testing whether an instruction actually changes agent behavior: fresh session, one-line request, watch what's skipped, edit one sentence, retest.

What changed in how instructions get loaded

A shorter section covers a real gap in that mental model: the built-in Explore and Plan subagents skip reading CLAUDE.md and skip checking Git status entirely, while custom subagents you define still inherit it. It also notes a new flag for passing subagent rules via a file instead of a command-line string, and a fix in recent releases removing an approval prompt that used to stall scripted runs using organization-delivered instructions — all catchable with the same /context check.


Last time you and the agent fixed one real thing in the shared-list app. Blank items no longer slip into the list. You read the diff, ran the test suite, clicked through the add-item journey yourself, added a regression test, and saved one commit with a plain message. Good. Now close that session.

Open a new one and ask for a second small change.

Watch what happens. The agent does not know how you start the app. It does not know which test command is the one that counts. It does not know what you mean by done. So it guesses. It reads a little of the project and invents a script name that does not exist. Or it runs a test file directly instead of the suite. Or it edits the wrong layer — puts a validation check in a component when the project keeps that logic somewhere else. Or, most commonly and most annoyingly, it announces that the change is complete without having run anything at all.

None of that is the agent being careless. Everything you explained by hand last chapter — the layout of the project, the command that matters, what finished means — lived in that conversation. The conversation is gone. The repository is what survived, and the repository never said any of it.

So we say it in the repository. That is the whole lesson: an agent is only as reliable as what the project tells it, and the durable win is not a cleverer prompt but a file the next session reads before it does anything else.

That file is called CLAUDE.md, and Claude Code loads it automatically. You do not attach it, mention it or paste it. When you launch a session inside a directory, Claude Code looks for that file and pulls its contents into the session's context before your first request arrives. Put it at the root of the repository, or inside the project's .claude folder — both are discovered. Because it sits in the repository, you commit it, and it travels with the code. A teammate who clones the project gets your instructions along with your source. A fresh session you start six weeks from now gets them too, without you remembering anything.

There is more than one place these instructions can live, and knowing which is which saves you from putting a private database password somewhere a hundred strangers can read it.

Start with the one you have just met: the project file in the repository, committed to Git, shared by everyone who works on the app. Facts about this project that are true for every person on every machine belong here.

Above that sits a personal file, in a folder called .claude in your home directory. That one follows you across every project you open. It is not committed anywhere. It is the right home for how you like to work rather than how the project works — that you want commit messages in lower case, say, or that you prefer to see a plan before any file gets written. If you find yourself typing the same sentence into unrelated repositories, that sentence belongs in the personal file. Claude Code will also read a rules folder alongside it, if you would rather split those preferences into separate files than keep one long one.

Below the project file sits a local variant, named CLAUDE.local.md, sitting in the repository root but not shared. This one is for facts true on your machine only: the connection string for the Postgres database you run locally, the port your dev server is on because something else already owns the usual one, a debug flag you personally like enabled. One warning that matters, because it has burned people. Claude Code does not add that file to your ignore list for you. If you create it and do not add its name to the project's .gitignore, you will commit your local connection string to a public repository. Do the ignore entry in the same minute you create the file.

There is a fourth layer you will probably never touch yourself, but it explains behavior you might otherwise find mysterious. A machine can carry organization-wide instructions installed at the system level — under a system application support folder on macOS, under a claude-code folder in the system configuration directory on Linux, under the installed program folder on Windows — and an employer can push those out centrally through a managed settings key. If you are on a work laptop and the agent seems to have opinions nobody in your team wrote down, that is where they came from.

Now, precedence. This is the part people get wrong because they expect it to work like configuration files usually do, where the more specific file replaces the more general one. It does not work that way. Claude Code does not overwrite anything. It concatenates. Every instruction file it finds goes into the context window in order, broadest first: the organization file, then your personal one, then the project one, then any subdirectory files on the way down to where you launched, then the local untracked one last.

So all of it is present at once, and conflicts are not resolved by a rule engine. They are resolved by the model reading the pile and reasoning about it. In practice the later, narrower instruction wins, because it reads as the more specific and more local statement — the same way a sticky note on one drawer beats a sign on the front door. But that is a tendency, not a guarantee. If a project rule genuinely has to override something in your personal file, do not rely on position alone. Say it forcefully in the project file. Write that the rule is important, or that the agent must do it. Blunt, emphatic wording in the narrow file is how you make sure a broad habit does not quietly survive.

One more mechanism before we write anything, because it saves the instruction file from bloating. A line beginning with an at sign followed by a path is an import. The contents of that file get pulled inline into the context when the instructions load. So you can point at your existing database schema notes rather than copying them into the instructions and then letting the copy drift out of date. Two practical notes. If you want to mention a file path in prose without importing it, wrap it in backticks and the import does not fire. And the first time a project imports a file from outside itself, Claude Code stops and asks you to approve it, because an import is a way for content you did not write to enter your session.

Now let's write the file for the shared-list app.

Do not start from a blank page. Claude Code has a command for exactly this: type slash init in the project, and it reads the repository — the package manifest, the build configuration, the test setup, the shape of the source tree, the Git history — and writes a first CLAUDE.md for you. Let it. That draft will be too long and partly wrong, and both of those are fine, because editing something down is faster and more accurate than remembering everything from nothing.

Then you edit. This is the real work, and it is mostly deletion.

Keep the commands that are not obvious. For our app that means the development server command, npm run dev, so a session knows how to bring the thing up. The type check, which is the TypeScript compiler run with the no-emit flag, because a session that only runs tests will happily leave you a type error. And the one test command that counts. Write down which one is authoritative — the whole suite, not a single file — and, separately, write down how to run a single test file when the agent is iterating, because that saves it guessing at the argument syntax. Also note how migrations run, whatever your tool is, because that is a command nobody infers correctly.

Now name the manual journey. The test suite is one check; a person clicking through the app is a different check, and last chapter you did both for a reason. Write the journey down in one sentence: open a list, type an item, add it, see it appear. That sentence is what turns "tests pass" into "the feature works," and if it is not in the file the agent cannot be expected to invent it.

Then two or three architectural constraints — real ones this app actually has, not general programming advice. Ours has three worth writing. Database access goes through the client in the database folder; no handler writes raw SQL. Validation for adding an item lives in one place, the place last chapter's fix put it, so nobody adds a second competing check in a component. And a schema change requires a migration file, never a hand-edited database, because a hand edit works on your laptop and nowhere else.

Finally, the definition of a finished change, stated flatly, because this is the sentence that stops the agent declaring victory early. A change is finished when the diff is small enough to read, the test suite passes, the manual journey has been clicked through, a regression test covering the specific mistake exists, and the work is one commit with a plain message. Say the commit format you want. Ours is the conventional style with a prefix like fix or feat.

That is close to the entire file. Not much longer than what you just read.

Now what to leave out, which is a longer list. Leave out the plainly discoverable. If the test command is just npm test and it sits in the manifest where anyone can see it, writing it down buys nothing. Leave out generic coding standards and anything that reads like framework documentation; the model already knows React, and your paraphrase of the docs will be worse than the docs. Leave out a file-by-file inventory of the project, which is stale the week after you write it. Leave out today's bug, this sprint's checklist, and any note that will be false within a month. And leave out full API dumps and long procedures.

There is one test for whether a line earns its place: include a rule only if leaving it out makes the agent make a predictable mistake. If you cannot name the mistake, delete the line.

The reason for that strictness is mechanical, not aesthetic. This file is not read once at startup and forgotten. It is re-injected on every single turn, as part of what the model receives with each request. A long instruction file is therefore a tax on every question you ask for the rest of the project's life. It eats the room the agent needs to actually read your code. Worse, it dilutes itself: as the instruction block grows, the model starts missing the subtle directives buried inside it, and the sentence you cared most about is the one that gets skated over. A bloated file also pushes the session toward compaction sooner, which means the conversation starts getting squeezed earlier than it needed to.

So aim for roughly two hundred lines at the root, on the order of a thousand tokens. Two useful helpers keep you honest. Slash context shows where your tokens are going and lists every instruction file currently loaded, under a memory files heading. Slash doctor audits the project and points out instructions that are redundant because Claude already infers them from the codebase — which is a fast way to find the lines your slash init draft added that you should now delete. There is also slash memory, which lists the active memory locations and opens the one you choose in your editor mid-session, so you can fix a bad sentence the moment you notice it rather than after.

Before the verification step, one distinction that this course will keep coming back to, and that people misunderstand about instruction files more than anything else.

CLAUDE.md is guidance, not enforcement. It is text handed to a language model with the hope that the model follows it. Usually it does. Sometimes it does not, especially if the file is long or the instruction is buried or two lines pull in different directions. Nothing in the system stops the agent from doing something your instructions forbid. If you write "never reset the database" and the agent is convinced a reset is the right move, that sentence is a request it can talk itself past.

Real limits live somewhere else: in the project's settings file, in the permissions deny list. That is not advice to a model. That is the runtime tool layer refusing to execute a matching command, checked before the command runs. A deny rule beats an allow rule and beats the model's intention. So for our app, the instructions say schema changes go through migrations, and the settings file denies the reset command outright, and denies dropping a database. The instruction explains the intent; the deny rule makes the worst case impossible.

Get this in your head now, because everything later in the course — hooks, hosted runs, unattended work — depends on knowing which of your rules are wishes and which are walls. Write the wish, then build the wall for anything you cannot afford to have gone wrong.

Proving a fresh session can do the work

Written instructions you have not tested are just an opinion about your project. So test them, and test them the way the failure actually happens: from nothing.

Commit the instruction file and the settings file first. Their own commit, with a message that says what it is — instructions and denies, not bundled in with a feature. Then start a genuinely fresh session. Not a resumed one, not the one still warm on your other tab. A new session, in the project directory, with no history.

Do one cheap sanity check before you ask for work. Run slash context and look at the memory files list. Your project instructions should be named there. If they are not, nothing else in this test means anything, and the usual cause is boring: the file is in a different directory than you think, or you are launched somewhere other than the project root.

Now the real test. Give a one-line request that only succeeds if the file was read. Ask for a second small validation rule on the same add-item form — reject an item longer than some sensible limit, say two hundred characters. One sentence. Say nothing about the test command, nothing about the layer the validation goes in, nothing about what done means.

Then sit on your hands and watch which things the agent does without being told.

Does it put the length check in the same place as the emptiness check, or does it invent a second validation site in the component? Does it run the authoritative test suite, by name, or does it make up a script? Does it run the type check? Does it add a regression test for the too-long case? And when it reports back, does it report in the terms your file defined — suite green, journey clicked, one commit — or does it just say the change is complete?

Each of those is a line in your file. Each answer tells you whether that line works.

Here is the part that matters more than the result. When the agent skips the manual journey, that is not the agent being lazy. That is your file being unclear, and the fix is in the file, not in the prompt. If you wrote something vague like "verify the change works," the agent has satisfied that by running tests. So go rewrite the sentence into something that cannot be satisfied any other way: state that a change is not finished until a person has opened a list, typed an item, added it and seen it appear, and that a passing test suite alone does not count.

Then throw the change away with git restore or git revert — you learned both last chapter — and run the same one-line request again in another fresh session. Same request, changed file. If the behavior changes, the sentence was the bug and you fixed it. If it does not, the sentence is still ambiguous, or it is buried too deep in a file that has grown too long, or it conflicts with something in your personal instructions pulling the other way. Make it blunter and more emphatic, or make the file shorter so it stands out, and go again.

That loop — request, observe the gap, edit one sentence, retest identically — is how an instruction file becomes trustworthy. It is the same discipline as the change loop from last chapter, aimed at the instructions instead of the code. Keep the request fixed so the only variable is the wording.

When it passes, commit whatever you edited. The repository now carries two things: a change, and the rules that produced it. A stranger cloning it gets both.

There is one thing this file still cannot do, and it is worth naming so you know where you stand. Instructions describe a project — its commands, its shape, its bar for done. They do not help the agent follow a code path it has never seen, and they do not help it reproduce a defect that somebody else reported in words. That is a different skill, and it is next.

What changed in how instructions get loaded

One recent change is worth your attention because it quietly breaks an assumption most people make about the file you just wrote.

The assumption is that every part of a session sees your project instructions. It does not. Anthropic's documentation now spells out the scoping rules for subagents — the separate, bounded helpers Claude Code spins up for particular jobs — and the two built-in ones behave differently from the ones you write yourself. The built-in Explore and Plan subagents skip reading CLAUDE.md files and skip checking Git status. That is deliberate: it keeps their context small and their responses fast. Custom subagents that you define do inherit the project instructions and the automatic memory.

Which task does that affect? Exactly the one you would least expect. If you write an architectural constraint into your instruction file — database access only through the client in the database folder, say — and then ask for a plan for some larger feature, the planning pass may not have that constraint in front of it. The plan comes back proposing raw queries in a handler. You then blame the instruction file, which never got read.

Smallest useful action, two parts. Run slash context and look at what is actually loaded and where your tokens went, so you stop guessing. Then, for a requirement that must survive an exploration or planning pass, put it in the request itself, or in the definition of a custom subagent, rather than trusting the project file to carry it. One sentence in the prompt is cheap insurance.

Two smaller items from the September releases, if you work in that territory. There is a new command-line flag, append-subagent-system-prompt-file, in version 2.1.261, which does what the existing string version did but reads the rules from a file on disk instead of a command-line argument. That exists because shells cap how long an argument can be, and a substantial set of review rules blows past that cap. If you run non-interactive passes — a batch code review, a sweeping refactor — put the subagent rules in a file inside your .claude folder and pass the path. The string and file forms are mutually exclusive, so pick one.

And in versions 2.1.219 and 2.1.234, organization instructions delivered through the managed claudeMd setting stopped raising an interactive approval prompt at launch. Previously that prompt could stall a scripted, non-interactive run — a script sitting there waiting for a keypress nobody was there to press. Shell and sandbox confirmations are untouched; only that one startup approval went away. If your team was wrapping Claude Code in a script to inject shared standards, distribute them through that managed setting instead.

The check for all three is the same and takes a second: slash context, and read the memory files list. If what you expect is named there, the mechanism worked. If it is not, no amount of careful wording in the file will matter.