dox: keep AGENTS.md current automatically
Your AGENTS.md is wrong three weeks later and the agent trusts it. Use dox to regenerate the file from the repo, then review the diff like code.
Why your AGENTS.md is wrong three weeks later
An AGENTS.md file goes stale because nothing connects it to the code. You write it once, by hand, on the day the repository looks a certain way. Then the test runner changes, a package is renamed, a service is deleted, and the file still describes June. Nothing fails, because no build step reads it.
The agent reads it and believes it. That is the part that costs you. A repository with no AGENTS.md makes a coding agent look around before it acts. A repository with a wrong AGENTS.md makes it stop looking, because it already has an answer. It runs the command your file names, the shell answers Missing script: "test", and now the agent starts guessing. Often it edits package.json to add the script your documentation promised. The stale file did not fail quietly. It caused an edit you did not want.
dox is one answer to that. It is a set of rules, written for the agent, that makes updating the documentation part of finishing the work, so the file changes in the same commit as the code that made it wrong.
What dox is, and what it is not
dox is a single Markdown file. The repository is agent0ai/dox, it is MIT licensed, and as of 11 August 2026 the whole project is one 3906-byte AGENTS.md, a README, a LICENSE and two images. There is no package to install and no runtime.
That matters, because the word generator suggests a program that parses your code. Nothing parses your code. dox is a contract your coding agent reads: your agent is the generator, and dox is the instruction set that tells it when to read the docs, when to rewrite them, and what shape each document takes.
The file has ten sections and two of them do the work. "Read Before Editing" tells the agent to walk from the repository root to every path it plans to touch, and to read every AGENTS.md along each route, in the current session, without relying on memory. "Update After Editing" tells it that every meaningful change requires a DOX pass, meaning a documentation update step run before the task counts as done. The pass updates the closest owning document when purpose, structure, workflow, permissions or user preferences changed.
The rest is shape. A child AGENTS.md has a default section order: Purpose, Ownership, Local Contracts, Work Guidance, Verification, and Child DOX Index. The root file holds project-wide rules plus the top-level Child DOX Index, which is how an agent discovers the child documents. "Closeout" is the checklist the agent runs at the end of a task: re-check the changed paths against the chain, update the nearest owning docs, refresh every affected index, delete contradictions, run existing verification, and report which docs it deliberately left alone.
Pin dox to one commit, not to main
The repository has no tags and no releases, so there is no version number to pin. Pin the commit instead. The current AGENTS.md is commit f34ec7ad1055d3393887e5a2670e8cb7320c9165, dated 1 August 2026.
mkdir -p .agent
curl -fsSL -o .agent/dox-f34ec7a.md \
https://raw.githubusercontent.com/agent0ai/dox/f34ec7ad1055d3393887e5a2670e8cb7320c9165/AGENTS.md
wc -c .agent/dox-f34ec7a.mdwc -c should print 3906. A different number means you did not fetch the file this guide describes, so read it before you trust it. If you mistype the commit hash, -f makes curl stop with curl: (22) The requested URL returned error: 404 and write no content, and wc -c then prints 0. A truncated file is worse than no file, because the agent follows half a contract without knowing it.
cp .agent/dox-f34ec7a.md AGENTS.md
git add AGENTS.md .agent/dox-f34ec7a.md
git commit -m "Add DOX rules (agent0ai/dox @ f34ec7a)"That cp is for a repository with no AGENTS.md yet. If you already have one, do not overwrite it. Put the dox sections above your existing content, keep your own rules underneath, and read the result once from top to bottom. Two documents that contradict each other produce an agent that follows whichever line it read last.
Then ask your agent, inside the repository, for the first pass. The README gives the exact wording:
Initialize DOX tree for this project now.It creates the child AGENTS.md files and the indexes that point at them. Check what it did before you believe it:
git status --short
find . -name AGENTS.md -not -path './.git/*' | sortEvery file in that find output should appear in a Child DOX Index somewhere above it. A child document that no index mentions is one the agent can miss, because the index is how it finds documents that do not sit directly on the path it is walking.
What dox can see, and what it cannot know
The agent building your tree reads the repository, so anything in the repository can go into the inventory: the directory layout, package manifests and lockfiles, the scripts in package.json or Makefile or pyproject.toml, CI workflow files, Dockerfiles, entry points, and CODEOWNERS if you have one. An inventory built from those is genuinely self-maintaining. When a package moves, the next pass moves the line that describes it.
Everything below is yours to state, because it is not in the repository to be read:
- why a rule exists, which is what stops an agent from removing it as unnecessary complexity
- which of two working paths is supported, and which one is waiting to be deleted
- anything outside the repository, such as the staging environment or the reason a dependency is pinned two versions back
- what you plan to do next week, which is the difference between a file that is current and a file that is useful
dox knows this about itself. Its own rules say Work Guidance must reflect the current standards of the project or the user's instructions, and that if there are none yet you leave the section empty. Verification must reflect an existing check, so with no test framework in the repo that section stays empty until there is one. A generated file that invents a standard is worse than an empty section, because the agent will then enforce the invention.
Keep the hand-written intent out of the generated inventory
This is the failure that makes people give up on generated docs. You write a paragraph explaining that the jobs queue must stay single consumer. Three weeks later a pass rewrites the file and your paragraph is gone, inside a diff of forty lines that mostly shuffle file names, and nobody catches it.
Two mechanisms, and you want both.
First, move durable intent into a different file. Design decisions and the reasoning behind them belong in a DESIGN.md written for the agent, and the notes that exist for people belong where you split HUMAN.md out of AGENTS.md. AGENTS.md then holds the inventory and the local contracts, which is exactly the part that should change when the code changes.
Second, fence the intent that has to stay inside AGENTS.md. Wrap it in markers and treat the block as human owned:
## User Preferences
<!-- dox:keep start -->
The jobs queue stays single consumer. Ordering is the reason this service exists.
Deploys ship on Tuesday. A Friday deploy is a human decision, not an agent decision.
<!-- dox:keep end -->Markdown comments do not render on the page, and the agent still reads them. Now make the block's survival checkable, so a pass that eats it fails loudly. Run this in CI (continuous integration) on every pull request:
git fetch -q origin main
sed -n '/dox:keep start/,/dox:keep end/p' AGENTS.md > /tmp/keep.head
git show origin/main:AGENTS.md | sed -n '/dox:keep start/,/dox:keep end/p' > /tmp/keep.base
diff -u /tmp/keep.base /tmp/keep.headdiff prints nothing and exits 0 when the block is untouched. Any output means the pass rewrote human-owned text, so a person approves it or reverts it. The check holds without anybody having to remember it.
Regenerate on the pull request, not on a timer
The best moment to refresh a document is the commit that makes it wrong. Put the DOX pass in the same pull request as the structural change and the diff stays small enough to actually read.
A blocking check that enforces it:
#!/usr/bin/env bash
set -euo pipefail
git fetch -q origin main
base=$(git merge-base origin/main HEAD)
changed=$(git diff --name-only "$base" HEAD)
if grep -qE '^(src|apps|packages)/' <<<"$changed" && ! grep -q 'AGENTS\.md$' <<<"$changed"; then
echo "Code changed but no AGENTS.md was touched. Run a DOX pass, or say why not."
exit 1
fiAdjust the paths to your repository. The value is that it fails on the branch, where the fix is cheap, and it fails for a reason a reviewer can act on.
A schedule is the backup, not the mechanism. A weekly job catches what nobody noticed on a branch: files moved by a rebase, a package deleted in a merge, a document naming a directory that no longer exists. Run it on a small box, the same one you might use to run a coding agent on a VPS, and have it open a pull request instead of pushing to main.
#!/usr/bin/env bash
set -euo pipefail
cd /srv/src/myapp
git fetch -q origin
git switch -c "dox/refresh-$(date +%Y%m%d)" origin/main
# Your agent CLI goes on the next line, in whatever non-interactive mode it offers.
# Prompt: "Run a DOX pass over this repository. Change AGENTS.md files only."
git add '*AGENTS.md'
git commit -m "dox: refresh AGENTS.md tree" || { echo "nothing to refresh"; exit 0; }
git push -q -u origin HEAD
gh pr create --fillThat comment is a placeholder on purpose. Every agent has its own CLI (command line interface) and its own non-interactive flag, and a command copied from a web page that does not match your version fails inside cron where nobody sees the error. Fill it in and run the script by hand once before you schedule it. The || exit 0 matters too: git commit exits non-zero with nothing to commit, working tree clean when the tree is already current, and under set -e that would report a healthy run as a failure.
Every pass costs tokens, because "Read Before Editing" makes the agent read the whole chain on each task. That is the trade, and it is worth watching if you are already counting what your agent runs cost.
Monorepos: many contracts, one index
One root AGENTS.md in a repository with forty packages produces a regeneration diff nobody reads, and a document that is mostly irrelevant to whatever the agent is doing right now. The dox answer is the Child DOX Index: the root holds repo-wide rules and points at its children, and each durable boundary owns its own file. How to lay that tree out, and which tools read nested files at all, is covered in nested AGENTS.md files for monorepos.
What dox changes is the review surface. A pull request touching packages/api should produce a documentation diff inside packages/api and nowhere else:
git diff --stat -- '*AGENTS.md'If that command lists six files for a one-package change, the tree is wrong. Either the boundaries are too coarse, or a rule that belongs in the root was copied into every child. dox states the fix directly: broad rules go in parent docs, concrete details go in child docs. Duplicated rules are what make a routine pass rewrite everything. If the same rules genuinely apply across separate repositories, that is a different problem, and sharing agent skills across repositories is the better tool for it.
Review the diff like code
A generated documentation diff is easy to approve without reading, which is how a wrong file ships. Read it with the suspicion you would apply to generated code, and look for four things.
- a command the file now names, which you should run yourself before you merge. Invented build instructions are the most common failure.
- a deleted line that carried intent. Additions are cheap. Deletions are where the loss happens.
- an absolute path, a hostname, an internal URL, or anything shaped like a credential
- an inventory entry for something that no longer exists, which
lssettles in a second
Then check the size with wc -l AGENTS.md. A root file past two hundred lines is a signal to split it, because the whole value of the chain is that the agent reads the small relevant part instead of everything.
When it breaks
The pass deleted your intent block. The diff check above prints the removed lines. Restore the file from the branch point with git restore --source=origin/main AGENTS.md, then re-run the pass with a narrower instruction naming the sections it may touch.
Two branches both regenerated. You get CONFLICT (content): Merge conflict in AGENTS.md and conflict markers <<<<<<< HEAD inside the file. Do not hand-edit the markers. The file is generated, so the correct resolution is a fresh pass over the merged tree.
The agent ignores the file completely. Check which filename your tool actually reads. If it reads a different one, point it at the same content with ln -s AGENTS.md CLAUDE.md and commit the symlink, so you keep one source instead of two documents that drift apart.
The tree grew children nobody indexed. Compare the find . -name AGENTS.md output against the index entries in the parent documents. A child that no index mentions is a child the agent can walk straight past.
When a generator is overkill
One package, one test command, two people who both know the repository: write the twenty lines by hand. A twenty-line AGENTS.md does not decay fast enough to justify a tree, an index, a CI check and a weekly job. Re-read it when you change the build. That is the entire maintenance cost, and it is smaller than the cost of the machinery around it.
dox is worth paying for when the repository has boundaries no one person holds in their head: several packages with different rules, or contributors who arrive without the background. The value is not the generated text. It is that the documentation becomes something a pull request can fail on, which is the only reason any file in a repository stays current.
FAQ
Do I need to install anything to use dox?
No. dox is one Markdown file, MIT licensed, and as of 11 August 2026 the repository ships no package and no releases. You copy its contents into your project's AGENTS.md and your coding agent follows the rules from there. Pin the commit you copied, f34ec7ad1055d3393887e5a2670e8cb7320c9165 at the time of writing, and name it in your commit message so you can tell later which version of the rules your tree was built under.
How do I stop a regeneration from deleting my hand-written rules?
Keep intent and inventory apart. Durable reasoning goes in a separate document, and anything that must stay inside AGENTS.md goes inside a marked block. Then check the block in CI: extract it from the branch and from origin/main with sed, compare the two with diff, and fail the build on any difference. A person then approves or reverts the change, instead of it passing unnoticed inside a large diff.
How often should I regenerate AGENTS.md?
On the pull request that makes it wrong. A structural change and its documentation belong in one diff, because that is the only moment somebody has the context to review both. A weekly scheduled pass is the backup for drift that slipped past a branch, and it should open a pull request rather than commit to main.
Should build commands live in the root AGENTS.md or in a child?
In the nearest document that owns them. Repo-wide rules and the child index live at the root. A command that applies to one package lives in that package's AGENTS.md. dox resolves conflicts by distance: the closer document controls local details, and no child may weaken a parent rule. Copying the same command into every child is what makes a routine pass rewrite the whole tree.
Is dox worth it for a small repository?
Usually not. One package with one test command and a twenty-line AGENTS.md decays slowly, and you can fix it in the minute after you notice. dox earns its cost when the repository has several boundaries with different rules, or contributors who lack the background, because then the chain of documents is doing work that no single person is doing.