When I was building FAF, I built it with Claude, starting on Sonnet 3.5 (yes, I thought 3.7 had come down from heaven!). I needed to test other AIs too. The Big 3 were Anthropic, Google's Gemini, and OpenAI, who had just released Codex.
All three got FAF from the get-go. It had a schema and it was YAML, and they love structured data. It's like YAML and noodles to AI π. Claude had CLAUDE.md, and Gemini reinforced that (copied it, lol) with GEMINI.md. But not OpenAI. There was no CODEX.md. They had AGENTS.md, and Codex kept telling me it "had context." I was fighting with an AI.
So I stopped using Codex for a while.
Then the industry chose. AGENTS.md became the file every coding agent reads: Codex, Cursor, Copilot, Windsurf, Amp, Devin. OpenAI gave it to the Linux Foundation's Agentic AI Foundation in December 2025, and it became an open standard.
Codex said it had context. I said it didn't. Then a developer from the Svelte community asked me, "Isn't this what AGENTS.md does?"
I had to sit back and look. And the penny dropped.
AGENTS.md is a guide for agents: how to build, how to test, the conventions, what not to touch. Instructions.
That's all it was ever meant to be, and it's good at it.
The argument I'd been having was never about which file wins. It was about something markdown doesn't do: say what the project is. What it's for, who it's for, what it's built on, what doesn't apply. Instructions need something to be about.
AGENTS.md is markdown, not YAML: prose, for instructions. project.faf sits upstream, next to package.json, and gives AGENTS.md the falsifiable facts about the project, on demand. AGENTS.md instructs the agents.
That's where the line came from:
FAF defines. MD instructs. AI codes.
becomes
FAF defines. AGENTS.md instructs. AI codes.
project.faf is a small YAML file at the root of a repo. It is to context what package.json is to dependencies, and it sits right next to it:
project/
βββ AGENTS.md β agents follow this
βββ package.json β npm reads this
βββ project.faf β AI reads this
βββ README.md β humans read this
βββ src/
It holds the facts: the goal, the stack, the who, what and why. Each one is a validated fact, from one of two places only:
faf auto reads (runtime, build, CI, hosting);faf go.
Nothing is guessed. A placeholder like tbd counts as empty, because it has no source.
It feeds the facts to AI, so tokens are best spent on following instructions and coding.
FAF defines. Context, compiled.
This matters more than it sounds. A 2026 study, Evaluating AGENTS.md: Are Repository-Level Context Files Helpful for Coding Agents? (Gloaguen et al., arXiv 2602.11988), found that "providing context files does not generally improve task success rates, while increasing inference cost by over 20% on average", for LLM-generated and developer-committed files alike. Their advice: use them for what's non-standard, and evaluate anything else before you rely on it.
Their latest version adds a finding that matters here: instructions in context files are well followed, but repository overviews, "although popular and recommended by model providers, are not helpful." That's the split this post is about. The paper doesn't say why. My read: an overview is, simply put, more prose. And prose can be stale, non-specific or contradictory, and an agent can't tell which.
So FAF doesn't write one. The definition stays in project.faf, where every fact has a source, and what lands in AGENTS.md is commands, guardrails and the stack: things an agent can run or check. The study tested markdown context files, so it shows the limits of prose. Structured data, a schema in YAML, wasn't in it. That's the right next test, and their advice says so too.
So: keep the file lean, and give the agent facts with a source.
That's how FAF writes it:
npx faf-cli export --agents
It writes the standard file, plain conformant markdown, never a FAF dialect. Inside it, FAF manages one block of facts: setup and build commands, how to run the tests, where things live, guardrails, the stack. Anything you wrote by hand outside that block is kept. When the facts change, run the same command and the block is rewritten from the source, so it doesn't drift.
Here's where any repo stands. With the ladder and the managed block, FAF and AGENTS.md work together, streamlined:
| NONE | GOOD | BETTER | BEST | |
|---|---|---|---|---|
| What it is | No AGENTS.md ; agents guess |
Any AGENTS.md |
An AGENTS.md with the managed block: lean, current, checked |
BETTER, plus project.faf in the repo: the source behind the block, and far more, with the app type and the human context (goal, who, why) |
| What happens | Every session starts blank | Goes stale, quietly | The block's facts have no source in the repo | Facts and instructions stay in step |
Most repos are in the first column. Getting to GOOD is one file. BEST is where the facts stop drifting, because they're defined in one place and every instruction file points back to it.
Here's the part I enjoy most. In September, Claude Code started reading AGENTS.md too: in a project with no CLAUDE.md, it uses AGENTS.md instead. The two camps I was choosing between have met.
Which makes the point for me. The file that instructs will keep changing names, vendors and rules. The facts underneath will not. Define them once, and let every instruction file, AGENTS.md, CLAUDE.md, GEMINI.md, .cursorrules, point back to the same source.
Paste either into your terminal or AI π
npx faf-cli auto
npx faf-cli export --agents
β bookmark repo: github.com/Wolfe-Jam/faf-cli
Then ask your agent what the project is. It won't have to guess.
Help guide what we build β
Comments Β· suggestions welcome.