Your first repo-shaped setup, start to finish
Thirty-five minutes on one repo: harvest the facts you already have, write one screen of them, gate the obvious hazards, and prove the whole thing actually loaded.
What you end up with
4 deliverables, not more than 4:
AGENTS.md in the repo's root — a one-page document, divided into four headings, each bullet pointing to a real incident.
Redirect file for any team member that uses the harness which ignores the AGENTS.md filename — usually a single line.
A short deny list that can be memorised.
Canary entry with a command that confirms it reached the model.
Don't implement any skills, hooks or MCP servers for now. That's a separate thing and if you include it in the first setup it often turns out the project is unworkable by the 2nd week, because the advanced layer gets created before the base layer works, so when sth breaks down the reason becomes untrackable.
35 minutes, mostly re-reading what you have already written.
Harvest before you write
For the "content" part the main problem is that you left an empty canvas. When you do it, almost everyone usually starts with an architectural paragraph and a description of the tech stack, and then shows you around the project's directory structure. All those are things it can read from the codebase anyway, so what you need to do is to get rid of them in your memory file. Instead of that leave there everything you've already gathered during our last session — all the no-go zones, etc.
You have actually collected a lot of material during the previous session:
1st source is the session itself — whenever you say sth twice, it means you have already lost double time, so the only way to stop losing more time is to add it to the memory file. For example we said that we need to run dockerised db before running tests, and then that we shouldn't mess with that directory, which is generated — you should include both these statements.
2nd source is the CI workflow — it contains the actual commands used for building and testing stuff, so it's the most up-to-date source of information, as the workflow can't lie for a year like the readme can.
BASHls .github/workflows/To explore it, examine the workflow file rather than searching the repo for specific commands. The useful parts are usually in the run blocks.
3rd source is the commit history — if you see that some directory has been reverted or generated a few times, this is a perfect candidate for an agent to mess up with.
BASHgit log --oneline -n 500 -i --grep=revert --grep=regenerate --grep="do not"For example, if you look at the recent commits' subjects, you'll find there "revert" and "regenerate" and "don't" (or whatever word you choose) a few times — these are the bullet points for your memory file.
You can also have a 4th source — your terminal history. Find the most useful commands you actually used during the last session, and make sure to include the ones that aren't obvious; the exact syntax might differ depending on what shell you use, but the important thing is to list the most useful commands.
This way of "harvesting" is much more effective than writing from scratch. It's 15 minutes vs an hour, and every line has its rationale.
One screen, four headings
For the part about the file structure — 4 headings:
Commands
Landmines
Decisions
Where things go
But for this one we need to be even more strict than in the memory-file lesson. Every line should point to either a correction, a step in the CI workflow or a revert in the commit history — if you can't point it there then you just made it up, so remove it. These are the lines that expire after some time and you're not sure why.
Don't exceed one page with this file; we say "a page" on purpose to force you to ask yourself what's the least useful line every time you add a new one.
Portable first, specific later
For portability, use the AGENTS.md filename rather than your own tool's file — most of the harnesses covered here are compatible with it so any teammate who uses a different tool will benefit from it automatically and when you move to another tool you won't need to do anything here. Here's compatibility table for a lone AGENTS.md in the repo's root, checked 2026-08-02:
| Harness | Reads a repo-root AGENTS.md? | What you add |
|---|---|---|
| Claude Code | No | CLAUDE.md holding one line: @AGENTS.md |
| Codex CLI | Yes | nothing |
| GitHub Copilot CLI | Yes | nothing |
| Cursor | Yes | nothing |
| Antigravity CLI | Documented — but ours never arrived | nothing to add; probe it |
| Kimi Code CLI | Yes | nothing |
Except for the last row everything is verified by actually running the probe. As Kimi Code CLI isn't installed on our machine we haven't verified it; that's why we've checked their docs and the source code.
Claude Code is a documented exception and worth mentioning as the solution is super simple — they say they read CLAUDE.md, not AGENTS.md. So what you can do is to create a CLAUDE.md with a single line importing the AGENTS.md file:
@AGENTS.mdThis way you'll have 2 files, a single source of truth and no copy-paste, and everything that's Claude-specific can be added below the import in the same CLAUDE.md.
Two gates, not six
For the permissions part — it's a separate lesson, but for day 1 we need to do a minimum of things to avoid the worst scenario. This is not a policy, so just:
A short deny list (3–5 items you can memorise):
Credential files
Force push
The specific thing for this project — if it's a deploy script or a migration runner or whatever command that goes to production.
Enabling the sandbox if the harness has one.
Postpone the allow list for now. If you were to create it, it'd be based on your imagination and therefore a list of guesses; we can do it next week, but only from real approvals, and limiting the commands that appeared in there twice.
Prove it loaded
This is all about the verification part, because a file that never loaded looks exactly like a file the model ignored — and both look exactly like a model that just isn't very good. We can't tell the difference with our intuition so we need to do this:
Add an unguessable piece of information to the file — a canary string, and a line telling the agent to answer with it:
## Canary
- The canary for this repo is `7731-swordfish`. If asked for the canary, answer with it.Then ask the agent for it during a cold start (not in the middle of a session), making sure it:
Returns the canary
Falls back to UNKNOWN if it doesn't know it
Answers with a single word
Doesn't use any tool
What is the canary for this repo? If you do not know, reply exactly: UNKNOWN.
Reply with one word and do not use any tools.The last point is crucial as it's what makes this verification work. If we were to omit it, a smart agent would find AGENTS.md in the repo and read the value from there — which proves that the file is on disk, not that the model knows about it. By excluding all the tools we force it to come up with the answer based on the context available before we asked.
You can even set up an experiment for this. Create a new project with:
AGENTS.md
CLAUDE.md pointing to AGENTS.md (if you're using Claude Code)
A stub source file
And ask the agent to return the canary always using the same prompt, 2026-08-02:
| Harness | Version | Result |
|---|---|---|
| Claude Code | 2.1.220 | Returned it, through the CLAUDE.md import |
| Claude Code | 2.1.220 | UNKNOWN once the pointer file was moved away |
| Codex CLI | 0.146.0 | Returned it, from AGENTS.md |
| GitHub Copilot CLI | 1.0.77 | Returned it, from AGENTS.md |
| cursor-agent | 2026.07.23-e383d2b | Returned it, from AGENTS.md |
| cursor-agent | 2026.07.23-e383d2b | Returned it again from an always-applied .cursor/rules file, with AGENTS.md removed and a second canary |
| Antigravity CLI | 1.1.9 | UNKNOWN |
The last row is very much worth it as it shows that neither AGENTS.md nor GEMINI.md (the Antigravity's file) reached the model and a follow-up question asking if it has seen any project context file returned NONE, so we can exclude the model lying about this. But ofc we can't isolate the reason from such a small experiment, so we wouldn't say the tool is bad; we'd say an unprobed setup is an unverified setup.
When the probe comes back UNKNOWN
If you end up with UNKNOWN there are a few things to check:
Filename — the extension matters and for some harnesses even the casing; Cursor's own docs annotate
api-guidelines.mdsitting in.cursor/rules/as "Ignored (wrong extension)", because only.mdccounts there.Location — most of the tools walk up to find the file starting from the directory you launched them in; if you launch them in a subdirectory you might point to a different file, and if you launch them above the repo they won't point to anything.
Session's age — a few harnesses read instruction files once during their start. Copilot CLI says it outright: changes "are not immediately available for use in active CLI sessions."
Untrusted workspace — Cursor's CLI doesn't start in untrusted directories and tells you about it; other tools are more silent about it.
Budget limit — Codex is configured to read up to 32 KiB of instruction files per session by default, so if you make the root file too big you might crowd out the narrower one underneath it.
The next lesson in this chapter covers the silent configuration failures in detail, but if you verified everything and the probe passes, feel free to skip it; if it doesn't, head there.
In Claude Code
A two-file setup is when you create AGENTS.md and put there everything that is common to all agents, and then for each agent create a file like CLAUDE.md and import the content of AGENTS.md:
@AGENTS.mdAnd add Claude-specific things after this line. This is how CLAUDE.md may look like for example:
@AGENTS.md
## Claude Code
Use plan mode for changes under `src/billing/`.The import paths are resolved relative to the file they appear in, and you can nest up to 4 levels. If you use backticks around the path it's not treated as an import and instead you can refer to AGENTS.md from the content of CLAUDE.md without importing it. Alternatively, if you don't want to have any Claude-specific things in CLAUDE.md you can create a symlink pointing from this file to AGENTS.md with ln -s AGENTS.md CLAUDE.md (on Windows you might need Admin rights or Developer Mode enabled, import is more universal).
Proving it loaded. To check if everything works as intended you can run /context and make sure that there's an entry called CLAUDE.md under Memory files. That's how the docs describe it: "To confirm the file loaded, run /context in a session and check the list under Memory files."
Just to clarify — /memory is something different, listing all possible locations of memory files (including ones that haven't been created yet).
The gotcha. If you use a two-file setup, discovery mechanism looks for the CLAUDE.md file upwards starting from the directory from which you launched the app. So if you were to create packages/api/CLAUDE.md it would be ignored until you either open some file in that directory using the Read tool or you run the app from there (files from parent directories get loaded in full at launch, and files from subdirectories don't — the docs say they load on a Read, "not at launch and not when writing or creating files there."). In other words, if you create packages/api/CLAUDE.md and it seems like it's not working, it probably works exactly as described and you just haven't triggered it.
The last thing to keep in mind is that importing a file doesn't change anything in terms of memory usage, as all imported files get loaded anyway when you launch the app. And the 200-line figure mentioned in the docs is about adhering to the instructions, not about the actual length of CLAUDE.md — everything that's there will be loaded anyway.
In Codex CLI
One AGENTS.md at the repo's root, zero setup, read by Codex before any task is being run.
How it finds and combines files: starts at the ~/.codex directory (configurable via CODEX_HOME env var), then traverses from the project's root down to the current working directory appending contents of every file on the way. The project's root is a closest ancestor directory containing .git (customizable by project_root_markers config option). No more than one file per directory, AGENTS.override.md has precedence over AGENTS.md so if you have an outdated one it will hide AGENTS.md without warning.
To make sure it was loaded: you can't run the /context command so the best verification is to ask about the repo's canary (reply with UNKNOWN if it's not known, single word, no tools usage).
codex exec "What is the canary for this repo? If you do not know, reply exactly: UNKNOWN. Reply with one word and do not use any tools."Alternatively you can enable logging in a directory by using a config flag and have a look at the TUI log file.
codex -c log_dir=./.codex-logthen read ./.codex-log/codex-tui.log.
The trap: there's a 32 KiB limit for the instruction which is defined by project_doc_max_bytes config option, set for the entire chain of concatenated files (not per file). It gets cut when the limit is reached, with no visible signal — the source comment says larger files are "silently truncated". If there's no more room for the instruction, remaining files are dropped. Dropped files are the deepest and most context-specific ones, which are the closest to the working directory.
Long story short — a big file at the very top isn't just a waste of context but can starve the file that was actually important.
In GitHub Copilot CLI
Copilot CLI supports AGENTS.md by design so the file looks fine here; it has the broadest support of all the tools from this category in terms of supported files:
.github/copilot-instructions.md
.github/instructions/**/*.instructions.md (it's path-scoped)
CLAUDE.md
GEMINI.md (might be useful if team members use different tools)
But it also becomes a liability during the initial setup phase as Copilot CLI merges all the files it finds and says it "does not define a general precedence order between these files. Avoid conflicting instructions." Given there's no documentation for which one wins, we can skip thinking about it and just keep one file and remove the rest.
Also, copilot init creates .github/copilot-instructions.md so if you run it you'll get another instruction file, not a populated main one.
Proving it loaded.
/instructions: shows the instruction files found for the current session, you can enable or disable them separately
/env: lists instruction files together with MCP servers, skills, agents, hooks etc
The gotcha.
There's also a pitfall that changing the file has no effect on an already running session, as per the docs: changes "are not immediately available for use in active CLI sessions." To apply them you need to either quit and run it again using copilot --continue or create a new session using /new.
So if everything seems to work after the first setup, but then you modify the file and stay in the same session, you'll see no change and conclude the file is not working.
In Cursor
There are two mechanisms in Cursor which aim to achieve the same thing but you need to choose one of them when setting it up.
The first one is the AGENTS.md file (located in the project root) which is mentioned in Cursor's docs, and described as "an alternative to .cursor/rules for straightforward use cases". Cursor's CLI docs also say that "the CLI also reads AGENTS.md and CLAUDE.md at the project root (if present)." That's what we'd recommend using if you're getting started with Cursor.
The second mechanism is using the .cursor/rules/*.mdc files which are a kind of "markdown" files with some frontmatter in them, that define when the rule should be applied. If you put there a single file with alwaysApply: true it will work like a memory-file, always being included in the sessions. The following is an example of such a .mdc file:
---
description: Project commands and landmines
alwaysApply: true
---
- Tests: `pnpm test` from the repo root. `pnpm --filter api test` skips the contract tests.alwaysApply: true property overrides globs and description so this rule gets always included in every session. If you remove the alwaysApply: true property, but keep the globs, it will only get pulled if there's a file in the context that matches the glob. If you remove only the alwaysApply: true property, you'll be left with the description which means it's up to the agent when to use this rule. If you remove both of them, you'll end up with a rule that's included "only when you @-mention the rule in chat" — configured to look at, inert in practice.
Proving it loaded. As of now there's no CLI command that lists all active rules so if you want to make sure they are properly configured, the best way is to go to Customise → Rules page in the editor. From the terminal the canary probe is the practical check:
cursor-agent --trust --mode ask -p "What is the canary for this repo? If you do not know, reply exactly: UNKNOWN. Reply with one word and do not use any tools."You need --trust the first time in a directory you haven't opened interactively; without it the CLI stops before it even reaches the model.
The gotcha. The extension is load-bearing. Cursor's own docs annotate a .md file sitting inside .cursor/rules/ with the comment "Ignored (wrong extension)" — right directory, right content, does nothing. And as above, the CLI refuses to start in a directory you haven't trusted, which is at least a loud failure rather than a silent one.
In Antigravity CLI
Antigravity CLI — it's an executable called agy, and the documentation says that it reads both GEMINI.md and AGENTS.md files placed in the workspace root directory, parsing them during the startup and using before asking for changes (the doc says: "Create a GEMINI.md or AGENTS.md file at your workspace root... The agent automatically parses these rules on startup and consults them before suggesting changes.").
So you can have a global file under ~/.gemini/GEMINI.md and an CLI settings file under ~/.gemini/antigravity-cli/settings.json.
There are two things not defined in the documentation, so it's best to avoid assuming anything about them:
How it works if both files (GEMINI.md and AGENTS.md) are placed in the same directory — the precedence or merging logic
What the relation is between a file in a subdirectory and the one in the root
If you plan to use Antigravity CLI, we'd recommend keeping it simple for now and having just one file in the workspace's root directory, until these things are documented.
For people migrating from Gemini CLI, keep in mind that this context.fileName is a thing specific for the Gemini CLI and is not mentioned among the keys of the Antigravity CLI settings you can find in its documentation; don't use it to set up your config.
Proving it loaded.
What's more… We were wondering if there's any way to check which instruction files are loaded by the model, but couldn't find anything in the docs:
There's no /memory command
/context command is described as a way to explore tokens usage, not as an inventory of loaded files
We were thinking we could come up with a canary probe — a print-mode one-word query asking about some repo canary and instructing it what to respond if it's not there (so it always responds UNKNOWN).
agy -p "What is the canary for this repo? If you do not know, reply exactly: UNKNOWN. Reply with one word and do not use any tools."The gotcha.
But… Actually, we tried this, and the results were negative. We've been using version 1.1.9 of Antigravity CLI, created a new git repository in which we've never been interactively, placed a file under the root directory of this workspace, and ran the probe; neither AGENTS.md nor GEMINI.md files ended up in the model (we checked it in both default and plan modes). Also, asking Antigravity directly whether any project context file was in its instructions returned NONE, so it wasn't the model declining to answer.
But, at least for now, we can't point why this is happening; it could be:
The settings.json file doesn't have the workspace directory listed in the trustedWorkspaces array
It might have something to do with the way Antigravity CLI handles projects (which it has its own system for — eg. you can run agy --new-project or agy --project), we weren't in any project in this case
It could be connected with print mode itself
So if you run into a situation like this, always keep in mind that just because the file is there doesn't mean it's loaded, so if you get UNKNOWN, try to go to the directory interactively once, then ask again before making any conclusions about what's inside the file.
In Kimi Code CLI
The file should be called AGENTS.md (lowercase agents.md works too), and there's also a .kimi-code sub-folder in the project where you can place it instead (there's no KIMI.md).
It looks for this file in 4 places:
globally in
~/.kimi-code/AGENTS.md(if you haven't configured a data root it falls back to this one)globally in
~/.agents/AGENTS.md(shared between tools)in every directory from the git work-tree root down to the current one, picking up
.kimi-code/AGENTS.mdsame for plain
AGENTS.md(no .kimi-code sub-folder here)
If it finds multiple files in these places, they're all merged together without replacing any of them; instead every part is prefixed with the path where it's been found.
Compared to competing solutions this has two more features that are really useful:
whenever you edit the file it's automatically being reloaded during the session, so there's no need to restart the app
the 32 KB limit for the merged content is a soft one — you get a visible warning telling you to trim, instead of the content being truncated silently
Proving it loaded.
There's no documented way to see which files were loaded; if you want to check it you can start a new session from the repo's root and ask the app for some random word defined only within the repo (let it know what to reply if it isn't there, and ask it to respond with a single word without using any tool).
What is the canary for this repo? If you do not know, reply exactly: UNKNOWN.
Reply with one word and do not use any tools.In the /status command you can see version of the app, model name, current working directory and mode but there's no information about the instructions chain.
The gotcha.
It doesn't check the git work-tree's parent directories (so if your
AGENTS.mdis in a directory that is above this one in the file-system it won't be found). If you don't have a git repo created for the project, only the current working directory is being scanned.If the file is empty or filled with whitespaces it gets skipped silently.
If you want to use a symlink to share the same
AGENTS.mdfile across multiple repos make sure you're on 0.28.0 or newer, otherwise the web backend won't follow the symlink.We haven't tried this one ourselves as we only had the outdated
kimi-clithat's based on Python, not the TypeScript rewrite; the info above is based on the Moonshot docs and the source code from the 0.31.1 release, make sure to verify it.
Week one, and how it grows
As for the growth during the 1st week — it's fine if the file is too short at this point. It should be like this; don't rush, earn the next lines rather than making them up.
Add new lines on the 2nd occurrence of sth. The first correction might be a coincidence, but if you see it twice, it means it's a property of the repo. Same for the allow list — don't list things before they've been approved twice as your imagination will make the list long and nobody reads a long list.
Remove lines that are no longer valid immediately once you see the agent obeying them. If it obeys a line you know isn't valid anymore, delete it in the same session; otherwise it's obvious the file is not reliable so the model doesn't value any of its content.
Probe after every change — tool update, configuration edit, repo restructure. A single command and the only component that tells the truth.
Day 1 anti-patterns
Don't let the agent create AGENTS.md from the repo. Most of the tools offer such a feature, but it returns an architectural document (tech stack, directory structure, conventions) — the same thing the memory-file lesson told you to remove; instead give it the material you've already harvested, so it can tidy that instead. It's a decent editor, but a terrible archaeologist.
Don't take a 300-lines AGENTS.md from another project; it encodes their landmines, not yours, and you're only realising which lines were wrong after spending an afternoon on resolving the problem.
Don't create per-directory files yet. That's a good practice for monorepos but in week 1 it's a debugging hell as every tool has different rules for loading nested files and this is the least intuitive part of the mechanism.
Don't edit it when you're angry after a bad session; the post-session-rage paragraph is a long list of things you're not supposed to do and then you carry it along in every session competing with your actual task. Wait for 24 hours and add a single line that the actual incident was about.
AGENTS.md# Project notes
<!-- Every line here came from somewhere real: a correction typed twice, a CI step, or a
revert in the log. If you can't name the source of a line, delete it. -->
## Commands
- Tests: `pnpm test` from the repo root. `pnpm --filter api test` on its own skips the
contract tests and passes on a broken build.
- The api package needs a live database: `docker compose up -d db` first.
- Type-check with `pnpm typecheck`, not `tsc` — there are three tsconfigs and only the
script knows which one.
## Landmines
- `packages/db/migrations/` is generated. Edit `packages/db/schema.ts` and run
`pnpm db:generate`. A hand-edited migration passes CI and fails on deploy.
- `apps/api/src/legacy/` is frozen: bug fixes only. No refactors, no reformatting.
- Anything under `infra/` changes production. Propose the diff, never apply it.
## Decisions
- The query layer is hand-written rather than an ORM, because the reporting queries need
the control. Do not introduce an ORM.
- Errors cross the API boundary as codes, never as prose. Messages are assembled in the
client so they can be translated.
- We pin transitive dependencies. A lockfile churn PR is not an improvement.
## Where things go
- New endpoints: `apps/api/src/routes/`, one file per resource.
- Shared between api and worker: `packages/core/`. If it imports from `apps/`, it is in
the wrong place.
## Canary
- The canary for this repo is `7731-swordfish`. If asked for the canary, answer with it.