Every fresh AI coding session could start the same way for us: the model greps around, opens a few files, builds a partial mental model of the stack, and fills the gaps with assumptions. Those assumptions are where it can 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.
We got tired of re-explaining the same five facts at the top of every session, so we committed a single 522-line AI_CONTEXT.md to one of our 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 our picture of the codebase instead of its own guess. It is the cheapest return we’ve found on AI-assisted dev, and it took an evening to write.
Here’s what went in it and why.
Rediscovery is non-deterministic
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 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 which router library you’re on 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 and 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 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 we 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 otherwise.
- Which platform files are off-limits. We 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
Dateobjects survive the RPC boundary) that looks like dead weight you’d be tempted to rip out if you don’t know it’s there.
None of that is discoverable in a way that survives session to session. All of it is one sentence in a committed file.
The lines that change behavior
This section is the point of the whole exercise. The most valuable lines in our AI_CONTEXT.md are not 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 we named so we’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.
We put those there because we know our own habits. Left alone with a capable assistant, we will happily spend an evening polishing a settings panel for a feature whose core value isn’t proven yet. And the assistant will help us, 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 we tell it. So we told it, in the file it reads first.
This is the move we’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 people’s heads and leak out one well-intentioned feature request at a time.
Why a flat committed file
You could put this in a system prompt, or a per-session preamble, or a tool that dynamically assembles context. We’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 blamewhen 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 records its own watch-outs. A “known watch-outs” section ages into a changelog of your own footguns, which is useful documentation for whoever maintains the code six months from now.
The same evening we wrote that file, we were 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 keeps the next session pointed at the actual goal and away from relitigating decisions we already made.
Writing your own
You don’t need anywhere near that many lines. Start with what you find yourself re-typing into the model. For us the minimum useful version is:
- A stack table. Ten rows, no prose.
- The one or two core abstractions, stated flat. If your whole app is “everything is a Node,” say that on line one.
- A list of files or directories that are off-limits or load-bearing-but-fragile.
- 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).
- 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.
If your sessions start from whatever the model guesses about your stack, data model and off-limits files, the product rules are the first thing to write down. An assistant will build a feature your priorities say to hold back, unless the file it reads first says otherwise.