Tecnología · August 15, 2026 · 10 min de lectura
AGENTS.md: the standard we hand every AI coding agent
How we run AI coding agents against one binding engineering standard: the 4D framework, a list of named failure modes, and why every behaviour ships behind a flag.
← Parte de Evaluaciones generadas por IA: la guía completa de 2026
En esta página
- One file, and every tool defers to it
- Four phases, always in the same order
- Discover: read before acting
- Define: success criteria before design
- Design and develop: grounded, in the local idiom
- Deliver: an output contract, and a self-check before finishing
- The list of named failure modes
- Green on the host
- Green on uncommitted
- Declared but never written
- The stray worker
- Correct by design, reported as a defect
- Everything ships behind a flag
- The loop that closes it
- Ask only at real forks
- What it does not fix
If you run AI coding agents against a production codebase, the constraint stopped being how much code they write a while ago. Ours will produce a week of work in an afternoon. The constraint is whether you can tell, afterwards, which parts of it are true. This is a note for engineering leads in that position, about the file we put in front of every agent before it touches anything: our AGENTS.md. Not a template to copy — an account of what happened when we tried.
Most published AGENTS.md examples are short and read like a style guide. Ours stopped looking like one around the point it started being written by things going wrong.
One file, and every tool defers to it
Every coding tool arrives wanting its own configuration file. Let each hold its own copy of the standard and you get three copies, then within a month three different standards, because nobody updates all of them at once. So AGENTS.md is canonical and binding on every tool that reads or edits the repository. The tool-specific files still exist, but carry only their own mechanics — entry points, dev ports, session quirks — and defer here for anything that counts as a standard.
This sounds like housekeeping and is not. An agent starting from a stale copy of the rules is worse than one with no rules, because it proceeds confidently and produces work that looks compliant. The failure is invisible at review time — the property every expensive failure below shares.
Four phases, always in the same order
All non-trivial work is structured as Discover, Define, Design and develop, Deliver. We call it 4D, which collides with a separate four-D framework we use on the hiring side for assessing AI fluency — different thing, same unfortunate initial. The phases are a shape rather than a checklist; what fills them changes per task. The order does not change, and neither does the rule that the first phase happens before anyone writes code.
The order never changes. What fills each phase changes per task.
Discover: read before acting
An agent works the sources of truth in dependency order and finishes at the current implementation and the live data, not at its own expectations. The output is an inventory answering, per field: always present, sometimes null, or omitted entirely — because omitted is not the same as null, and code that treats them alike is a defect waiting for a quiet afternoon. Is it gated by plan or by permission. Is it populated today, or declared and empty pending a feature nobody built.
Claims cite a file and a line. Facts that can be trusted are kept separate from facts that may have drifted and need re-verifying. That separation is unglamorous, and it is the highest-yield instruction in the file.
Define: success criteria before design
Checkable one-liners, written before anything is designed. The task names the failure modes it designs against, and states what is explicitly out of scope. Out-of-scope is the line most often skipped, and the one that stops an agent helpfully refactoring a module nobody asked it to touch.
Design and develop: grounded, in the local idiom
The absolute rule is that an agent never silently invents a field, a behaviour or a number. If something it needs does not exist, it reports that as an explicitly labelled gap and files a request. Absence is honest. Inference presented as fact is not.
Never silently invent a field, a behaviour or a number. Absence is honest; inference presented as fact is not. If what you need does not exist, label the gap and file it — never paper over it.
Code follows the existing standards of the codebase it lands in: idioms, naming, layering, comment density, shared primitives, test patterns. This one is specifically an agent problem. Models have a house style of their own and, left alone, will apply it — competent code that reads as though it arrived from somewhere else, which is exactly what a reviewer's attention slides off. Our notes on prompt engineering for software engineers cover the individual's side of the same habit.
The product ships in six languages, so anything touching text is in scope for all six. Whitespace word counts and ASCII-only regular expressions are the recurring landmines.
Deliver: an output contract, and a self-check before finishing
The brief names the sections the final report must contain: file-and-line summaries, exact test counts against a stated baseline, deviations with reasons, proven claims kept apart from unproven ones. The agent runs a self-check against that contract before declaring itself done. Stating the baseline matters more than it sounds — "all tests pass" is not a fact until you know what passing looked like yesterday.
The list of named failure modes
If I could transplant one section of this file into another codebase, it would be this one. When a defect pattern turns up twice it gets a name, and the name joins a list inside the same file every agent reads before starting — not a postmortem document living somewhere respectful and unread.
Naming does real work. An agent told to be careful about tests does nothing with that; one told to check for green on the host has something to look for. None of it was planned; the list grew because things broke.
Green on the host
A test that passes because of a property of the machine rather than the code. Python's time.monotonic() counts from an unspecified reference point, which on Linux is system boot. We used 0.0 as a "never synced" sentinel — so the comparison measured host uptime.
_last_synced = 0.0 # sentinel: "never synced"
def needs_sync() -> bool:
# On a laptop up for a week this is ~600000, so it never syncs.
# On a fresh CI runner it is ~30, so it always does.
return time.monotonic() - _last_synced > SYNC_INTERVALIt held on a development machine up for days and failed on a fresh CI runner. In production it silently suppressed the first sync for roughly the first hour of every pod's life — the sort of bug that never pages anybody and quietly makes your data slightly wrong for a year. The rule that came out of it: if an assertion would change on a rebooted machine, in a different timezone or on a different filesystem, it is testing the host.
Green on uncommitted
A suite that passes against a working tree containing files the commit leaves out. One of ours shipped a producer and a test asserting its output, but not the schema that typed it. Every local run was green; the committed tree could not pass its own committed test, and the deployed endpoint raised on that path. The guard is boring and works: run the suite against a clean checkout of HEAD before you believe it.
This is sharper with agents. One that has been editing for an hour sits on a working tree only it understands, and it is the same entity deciding what to stage.
Declared but never written
A field or table that is typed, serialised and perfectly readable, and that nothing in the application ever writes. From the outside it is indistinguishable from a field that failed to populate, so everything built on top inherits the silence. Our rule now: a field is either populated or removed. Honest absence is a documented null, never an always-empty value.
Agents are unusually productive at generating these, because scaffolding a field end to end — type, schema, serialiser, accessor — is exactly the tidy symmetrical work they are good at. The writer is the boring part, and it goes missing.
The stray worker
To a message broker, a worker started outside the cluster is indistinguishable from a legitimate pod. It polls the same queues and takes real work, including paid model calls. One of ours ran for hours on a laptop against a shared namespace, because a reconstructed environment file had inherited the cluster's host while every other endpoint in it pointed at localhost. The guard: refuse to start unless in-cluster.
Correct by design, reported as a defect
The costlier direction of error, and the one I would watch for first. A finding taken from an aggregate query, written up before anyone checks the dimension that explains it. A set of rows looked wrong; grouped by one more column, every one was correct by design. One GROUP BY would have settled it.
It is worse than missing a real bug because the "fix" is a guard that fires on correct behaviour, and such a guard teaches your operators to ignore alerts. You do not get that trust back cheaply. So before anything is labelled a defect, the agent finds the code path producing it and asks whether there is a design reason it is correct — the "can it fail?" discipline pointed at your own claim.
Mark every claim proven or inferred. Nearly every wrong call in this repository's history was an inference stated as a finding.
Everything ships behind a flag
Every feature, every behaviour change, every new surface is flag-managed. Not usually — as a default argued out of, with the argument written down in the commit.
There are two kinds, and a feature normally needs both. An entitlement gate asks whether a customer is entitled to this at all; commercial owns it. A kill switch asks whether it should be running right now; operations owns it. Confusing the two is how you end up at three in the morning unable to stop a misbehaving job without touching billing or shipping a deploy.
Is this customer entitled to it at all?
Flipped by: Commercial
Should it be running right now, this minute?
Flipped by: Operations
Did they actually ask for it to be live?
Flipped by: The customer
Three separate questions. Collapse them into one boolean and you can answer none of them.
Customer intent is a third and separate thing: a customer setting something to published is not a flag, it is them saying yes, publish mine. Kill switches also default to on and are named for what turning them off does, so a missing environment variable is the working state.
The one exception: security controls get no off switch. A flag that disables a bot gate, a tenancy check, a rate limit or an authentication check is a vulnerability with a configuration key attached. If a control exists to stop an attacker, the attacker must not be able to hope it is switched off.
Flags are debt. Each is a branch tested in both states and removed once the rollout finishes, so the removal condition gets stated in the commit that introduces it — otherwise you rely on somebody remembering, and nobody remembers. Where a change touches automated decisions about people, this discipline is roughly what the EU AI Act's oversight requirements expect you to produce afterwards.
The loop that closes it
Deliver feeds the next Discover. Every finished task is meant to improve the system that produced it, in five specific ways:
- Correct upward. When the work proves a brief or a standard wrong, fix the source rather than routing around it. An agent that finds drift reports it as a result, not a footnote.
- Name new failure modes. A pattern seen more than once gets a name and joins the list. That is how every entry above arrived.
- Turn fixes into guards. A recurring fix becomes a regression test or a lint rule. The same bug should never need the same diagnosis twice.
- Feed the self-check. Anything a self-check missed becomes a new self-check item, so the checklist is grown by its own failures.
- Leave the trail. Durable learnings land in the repository's standards, not in a session that ends.
The last matters far more with agents. A colleague accumulates context over months and carries it between conversations; an agent starts from zero every session. Whatever is not written down did not happen.
Ask only at real forks
When a decision genuinely changes the outcome — product semantics, scope, something irreversible, money — the agent asks one crisp question and waits. Everything resolvable from the code or an obvious convention gets resolved, with the choice noted. Both directions are errors: asking what the codebase could have answered burns the reviewer's attention, and not asking at a real fork produces confident work pointed the wrong way. The second is far more expensive.
What it does not fix
It does not make an agent good at judgement, and it does not remove review — if anything it raises review's value, because throughput went up and the reviewer is now the scarce part. It does not stop an agent being confidently wrong. What it changes is how quickly somebody notices, which turns out to be most of the benefit.
The file has a running cost too. It is long, it must be maintained, and a standard nobody updates becomes one everybody quietly routes around. We have had to correct it more than once because the work proved it wrong — the loop functioning rather than failing, but real effort either way.
And it works for us, on this codebase, at this size, with these people. I would be sceptical of anyone presenting a file like this as a framework to adopt wholesale, mine included. The transplantable part is not our list of failure modes — yours will differ, because your systems break differently. It is the habit of naming what broke and putting the name where the next agent has to read it.
There is a symmetry we did not design and have come to like. The properties we demand of an agent's report — grounded in what exists, proven kept separate from inferred, absence stated honestly — are the ones we demand of a candidate evaluation. Both are claims about capability somebody will act on. That is the argument behind AI-native hiring, and why assessing how someone works with AI tools is part of how we hire engineers. The good ones treat model output as a draft to be checked. This file is that instinct, made compulsory.
Escrito por
Jakir Patel · Founder, Hanzomon
Building H-Evaluate — AI-native, quality-gated hiring assessments. Writes about assessment engineering, hiring integrity and compliance-first AI.