Every fresh AI coding session can start the same way: the model greps around, opens a few files, builds a partial mental model of your stack, and fills gaps with assumptions. Those assumptions are where it can quietly help you build the wrong thing. It guesses your data model from a couple of type definitions. It infers your conventions from whatever file it happened to open first. It has no idea that the feature you’re about to ask for is the exact thing you’ve told yourself not to build yet.

I got tired of re-explaining the same five facts at the top of every session, so I committed a single 522-line AI_CONTEXT.md to one of my repos. It’s a README written for the assistant instead of for humans. The next time a fresh Claude or Cursor session spun up, its first read was my picture, not its guess at my picture. Cheapest leverage I’ve found on AI-assisted dev, and it took an evening to write.

Here’s what went in it and why.

The problem it solves

When you don’t pin the context, the model rediscovers it. That sounds harmless because the rediscovery is usually fast and usually mostly right. The trouble is “mostly.” A grep-derived mental model is non-deterministic: two sessions on the same codebase can land on two different and equally confident pictures of how things fit together, and you won’t notice the divergence until one of them writes code against the wrong abstraction.

The fix is to stop making the model derive what you already know. Write it down once, commit it, and let the first read of every session be the same correct file instead of a fresh archaeological dig.

What actually goes in it

The doc I wrote covers four layers. Three of them are the obvious ones. The last is the one that earns its keep.

The stack, as a table. A layer-by-layer rundown of what’s running: the RPC layer, the ORM, the database, the frontend framework, the router, the styling system, the bundler, the package manager. Write it as a table rather than prose. The model reads it in one pass and stops guessing whether you’re on one router library or another by inspecting import paths.

The data model, stated once. This app’s whole abstraction is that everything is a Node with a lifecycle state (active, waiting, dormant, resolved, and a couple of variants), and nodes are connected by Edges. That’s the single most load-bearing fact in the codebase, and it’s exactly what a model half-infers from a schema file and gets subtly wrong, calling a state a status, missing that Edges carry the real structure. Stating it flat at the top means every session reasons about the right shape from the first token.

The router and function map. Every server function in the data layer, every frontend page, what each one is for. This is the part that reads most like a generated artifact, and honestly you could generate a chunk of it. The value is that the model doesn’t have to open ten files to learn that a given function exists and what it returns.

The stuff a model would get wrong. This is the section I didn’t expect to matter as much as it does. It records load-bearing trivia that no amount of grepping would surface correctly:

  • The repo is named one thing and the product is named another, because the rename is deferred until after a validation milestone. A model reading the repo name will confidently use the wrong product name everywhere unless you tell it not to.
  • Which platform files are off-limits. I marked a core directory as “do not touch casually” so the assistant doesn’t go refactoring the plumbing while fixing a button.
  • Auth is one provider in production and a local bypass in dev. A model will otherwise assume whatever the first auth import suggests.
  • A serialization detail (the library that lets Date objects survive the RPC boundary) that, if you don’t know it’s there, looks like dead weight you’d be tempted to rip out.

None of that is discoverable in a way that survives session-to-session. All of it is one sentence in a committed file.

The part that actually changes behavior

This section is the real point of the whole exercise. The most valuable lines in my AI_CONTEXT.md aren’t technical at all. They’re product discipline, written as non-negotiable rules the model reads before it reads anything else.

The app is at its earliest milestone, and the entire goal of that milestone is to validate one thing: the retrieval pipeline that builds a context pack for a query. So the doc says, in as many words:

V0 primary goal: validate the retrieval pipeline through daily use. Success = relevant 80%+ of the time. If not, refine retrieval. Do not add UI features.

And then, even more bluntly, a rule I named so I’d have to confront it:

The Discipline Rule: if the context pack doesn’t materially improve AI conversations after 14 days of real use, refine retrieval before building anything else.

I put those there because I know myself. Left alone with a capable assistant, I will happily spend an evening polishing a settings panel for a feature whose core value isn’t proven yet. And the assistant will help me, enthusiastically, because “add a nice UI for X” is a clean, satisfying request it can fully satisfy. It has no way of knowing that building that UI is the wrong move this month unless I tell it. So I told it, in the file it reads first.

This is the move I’d push hardest on. A coding assistant is very good at doing what you ask and has zero opinion about whether you should be asking. Every rule you keep repeating to yourself (“don’t add UI until retrieval is proven,” “prove the expensive thing before you decorate it”) is a rule the model will cheerfully help you violate unless it’s written down where the model looks. The context file is the cheapest place to encode product judgment that would otherwise live only in your head and leak out one well-intentioned feature request at a time.

Why a flat committed file beats the alternatives

You could put this in a system prompt, or a per-session preamble, or a tool that dynamically assembles context. I’ve tried versions of all of those. A plain committed Markdown file wins for boring reasons:

  • It’s version-controlled, so it evolves with the code and you can see in git blame when a rule got added and why.
  • It can be shared across sessions and tools. Whether an assistant reads it automatically depends on that tool’s configuration, but a committed file gives every workflow the same source of truth.
  • It can be made an explicit first read. When session instructions or tooling point to it, the model starts from your documented picture rather than a fresh reconstruction.
  • It’s honest about itself. A “known watch-outs” section ages into a changelog of your own footguns, which is useful documentation for the human version of you six months from now.

The same evening I wrote that file, I was reworking a retrieval scoring formula and collapsing a cluttered detail view down to one state-aware line per row. Both of those are exactly the kind of work where, three sessions later, an assistant would want to “improve” things back toward complexity. The context file is what keeps the next session pointed at the actual goal instead of relitigating decisions I already made.

How to write your own

You don’t need 522 lines. Start with what you find yourself re-typing into the model. For me the minimum useful version is:

  1. A stack table. Ten rows, no prose.
  2. The one or two core abstractions, stated flat. If your whole app is “everything is a Node,” say that on line one.
  3. A list of files or directories that are off-limits or load-bearing-but-fragile.
  4. The trivia that contradicts what the code looks like it’s doing (repo name vs product name, prod auth vs dev auth, the library that exists for a non-obvious reason).
  5. Your discipline rules. The things you keep telling yourself not to do. Write them as rules, not suggestions, and name them so you can’t pretend you didn’t see them.

Commit it. Reference it at the top of your sessions, or let your tooling pick it up automatically if it does that. Then every time you catch yourself re-explaining something to the model, or correcting a wrong assumption it made, add the correction to the file instead of just fixing it in the moment. The file gets sharper, and the rediscovery problem shrinks every week.