DEV Community

Cover image for AGENTS.md: the one file that briefs the agents on our repo
Yonto Player
Yonto Player

Posted on

AGENTS.md: the one file that briefs the agents on our repo

I build Yonto Player, a media player for your own library on Android TV, phones and tablets, with AI coding agents. In my experience agents start every session knowing nothing about the repo, so the first thing they read decides a lot of what they do next. That first thing is one file at the root, AGENTS.md, and I have spent more time editing it than almost any other file.

The series' opening post showed a small sketch of it. This one goes deeper: what goes in, what stays out, and how it is written.

What the file is for

It is a contract between me and an agent that has never seen the project. It has to answer, quickly, the questions a new teammate would ask on day one: what is this, how is it laid out, how do I check my work, what must I never break, and where do I read more for the job I was given.

It does not try to be the documentation. The root file holds what nearly every job needs. Everything that only one kind of job needs lives in a deep-dive, and the file says where.

What goes in

Six things, in roughly this order.

1. The product in a paragraph, and its one rule. Without this, an agent invents an architecture. Mine says the app is a native Android TV app, that the content source is a black box behind one interface, and that every third-party source is a JavaScript plugin. That one sentence prevents a whole class of change, like a screen that asks "is this Jellyfin or Plex?". The developer docs describe the plugin side of it.

2. The module layout. A tree with one line per module, saying what it holds and what it must not. In my experience a dependency rule that is written down gets followed far more often than one an agent has to infer.

3. The exact build and test line, and why. Not "run the tests", but the full command, because in a multiplatform project a short command quietly skips modules. A sketch of how I write it:

## Build & test

Full verification before every commit, every module:

    ./gradlew clean assemble test lint <platform checks> --rerun-tasks

Quick loop for one module:

    ./gradlew :feature:assemble :feature:test :feature:lint

Why: a multiplatform library has no plain `test` or `lint` task of
its own, and Gradle does not run a task name in modules that lack it, so a
line without the right names skips that module in silence.
Enter fullscreen mode Exit fullscreen mode

The "why" is the part that matters. An agent that knows the reason will keep the line correct when a module is added, and one that only has the command will not.

4. The standing rules. Few, and each with its reason. My favourite is that docs change in the same pull request as the code, never as a follow-up, because the docs are what the next agent session reads, so a doc left describing old behaviour is a trap and not just a stale page.

5. Comment discipline. Agents love to comment. The file says: default to none, write one only for what the code cannot say, such as a workaround or a hidden constraint, and put the story in the commit message.

6. Pointers. This is the part I would copy first: a table that names a deep-dive for each kind of job, covered in the next section.

The "read this when" table

At the bottom is a table with one row per kind of job. Here is the idea, with generic names:

## Read this when

| Read this when...                          | File                       |
| ------------------------------------------ | -------------------------- |
| a build, test or lint fails                | docs/agents/build.md       |
| you touch the TV UI or its focus handling  | docs/agents/tv.md          |
| you write a test                           | docs/agents/testing.md     |
| you add code to the core                   | docs/agents/architecture.md|
Enter fullscreen mode Exit fullscreen mode

There are 16 rows in the real one, pointing into a folder of deep-dives, docs/agents/, one file per topic. An agent fixing a focus bug on the TV reads the TV file and nothing about the phone, the build or the plugin runtime. The deep-dives are longer than AGENTS.md several times over, and almost none of that ever enters a given session.

That is the real purpose of the table: context is a budget. Everything in the root file is read every time, so it earns its place only if nearly every job needs it. Everything else is a pointer, and a pointer costs one line.

How a rule is written

A bare rule gets followed until it is inconvenient, and then it gets reasoned around. So I write each rule as the rule, then the reason, then a date. A sketch:

**Keep code comments short** (decided 2026-09-23).
Default to none; well-named code says what it does.
Write one only for what the code can't say, in a sentence or two.
The story belongs in the commit message, and a durable rule
belongs in this file.
Enter fullscreen mode Exit fullscreen mode

The date matters more than it looks. The date lets an agent, or me, tell which of two rules is newer. It also lets me change the live line instead of adding a correction beside it, so the file never says two things.

What stays out

  • Anything only one job needs. That goes into a deep-dive.
  • History. "We used to do X" belongs in a dated design doc, of which the repo has 59.
  • Long how-tos. Workflows such as a quality round, fixing one issue or opening a pull request are skills, plain Markdown any agent can follow, listed in skills/README.md. The file points to them and does not repeat them.
  • Anything that is easy to derive from the code. If a quick search answers it, a line in the contract only adds a chance to go stale.

Keeping it short

The root file tends to grow, because every lesson wants a line. My test for a new line is: would an agent doing an unrelated job be worse off without it? If not, it goes into the matching deep-dive and the table already points there. When a section only matters to some jobs, I move it out and leave one sentence and a link.

I also changed what "done" means, since the rule about docs says a change whose docs are not written is unfinished. Writing it into the contract made it an explicit part of the job.

What to take away

If you want to try this on your own repo, start small:

  1. Write the product and its one rule in a paragraph.
  2. Paste the exact verification command and say why it is shaped that way.
  3. Add three rules, each with its reason.
  4. Make a table of jobs and files, even if the files are short.

Then watch what the agents get wrong, and add the line that would have prevented it, or better, the pointer to the page that explains it.

Read more

Top comments (0)