Como organizar AGENTS.md aninhados num monorepo
Veja como dividir um AGENTS.md de 600 linhas em regras locais para cada serviço, reduzindo contexto desperdiçado e mantendo comandos e convenções atualizados.
O que significa usar AGENTS.md aninhados num monorepo
Usar AGENTS.md aninhados num monorepo significa ter um ficheiro pequeno na raiz do repositório e outro ficheiro dentro de cada diretório de serviço. O ficheiro da raiz contém as poucas regras válidas em todo o repositório, além de um mapa com a localização dos outros ficheiros. Cada ficheiro de serviço contém os comandos e as convenções específicos desse diretório. Um agente que edite services/worker/queue.py lê o ficheiro da raiz e o ficheiro do worker, sem gastar contexto com o front end que nunca irá alterar.
Não há nada para instalar. AGENTS.md é uma convenção, e o projeto upstream explica isso claramente:
AGENTS.md é apenas Markdown padrão. Use os cabeçalhos que quiser; o agente simplesmente analisa o texto que fornecer.
Por isso, vale a pena aprender corretamente esta técnica. O formato não mudará sem aviso. O que causa problemas é a localização e a manutenção dos ficheiros, e ambas são da sua responsabilidade.
Por que um único AGENTS.md grande na raiz deixa de funcionar?
Um único AGENTS.md com 600 linhas na raiz de um repositório que contém uma aplicação Web, um worker em segundo plano e um diretório Terraform falha de quatro formas diferentes.
Fica desatualizado porque ninguém é responsável por ele. O engenheiro que renomeia um script de testes em apps/web está a editar ficheiros em apps/web. O AGENTS.md da raiz não faz parte desse diff, portanto nenhum revisor vê a inconsistência. Seis semanas depois, o ficheiro descreve uma etapa de compilação que já não existe, e a pessoa que causou o problema já se esqueceu da alteração.
Consome contexto em todas as tarefas. Estes ficheiros são carregados no início da sessão, antes de o agente saber o que lhe vai ser pedido. A documentação do Claude Code apresenta um valor concreto: "target under 200 lines per CLAUDE.md file. Longer files consume more context and reduce adherence." O Codex deixa de fundir ficheiros de instruções quando o tamanho combinado atinge 32 KiB, o valor predefinido de project_doc_max_bytes. Um ficheiro na raiz que documenta quatro serviços consome esse orçamento com três deles em todas as tarefas.
As instruções começam a contradizer-se. O diretório Web requer pnpm test. O worker requer pytest -q. Escrita num único ficheiro, cada regra só está correta em algumas situações, portanto o agente tem de adivinhar qual se aplica. A documentação do Claude Code descreve o resultado: "if two rules contradict each other, Claude may pick one arbitrarily." Um ficheiro por diretório elimina essa dúvida, porque apenas uma das duas regras está presente no contexto.
Fica cheio de factos que o agente pode obter a partir do código. Uma árvore de diretórios, uma lista de dependências, um resumo da função de cada pacote. A verificação /doctor do Claude Code existe para remover exatamente este conteúdo. Ela "cuts content Claude can derive from the codebase, such as directory layouts, dependency lists, and architecture overviews" e mantém "pitfalls, rationale, and conventions that differ from tool defaults." Essa frase é o melhor teste que conheço para determinar se uma linha pertence realmente ao ficheiro.
O agente lê o ficheiro na raiz ou apenas o mais próximo?
É aqui que a maioria das pessoas entende mal o comportamento do modelo. Por isso, vale a pena citar a convenção a montante em vez de a parafrasear:
Coloque outro AGENTS.md dentro de cada pacote. Os agentes leem automaticamente o ficheiro mais próximo na árvore de diretórios. Por isso, o ficheiro mais próximo tem precedência e cada subprojeto pode incluir instruções adaptadas.
Sobre os conflitos:
O AGENTS.md mais próximo do ficheiro editado prevalece; os pedidos explícitos do utilizador no chat substituem tudo o resto.
Para muitas pessoas, "tem precedência" significa que "o ficheiro na raiz é ignorado". Não é isso que acontece. Nas ferramentas que implementam esta convenção, todos os ficheiros no caminho entre a raiz do repositório e o diretório de trabalho são lidos e concatenados. O ficheiro mais próximo só prevalece quando dois ficheiros definem regras diferentes para o mesmo assunto.
O Codex descreve explicitamente este mecanismo: "O Codex concatena os ficheiros da raiz para baixo, separando-os com linhas em branco. Os ficheiros mais próximos do diretório atual substituem as orientações anteriores." O Claude Code percorre o mesmo caminho para o seu próprio nome de ficheiro. Os ficheiros nos diretórios acima do diretório de trabalho "são carregados integralmente no arranque" e "todos os ficheiros encontrados são concatenados no contexto, em vez de se substituírem". Os diretórios abaixo do diretório de trabalho comportam-se de forma diferente: o Claude Code carrega esses ficheiros a pedido, "quando o Claude lê ficheiros nesses diretórios".
Daqui resultam duas consequências práticas. O ficheiro na raiz é um prefixo de todas as sessões no repositório. Por isso, trate cada linha como uma linha cujo custo se repete uma centena de vezes por semana. Um ficheiro por diretório não tem custo quando o agente trabalha noutro local. Assim, os detalhes devem ficar nesse ficheiro.
Este comportamento foi confirmado na documentação do Codex e do Claude Code em agosto de 2026. As ferramentas implementam a convenção de forma ligeiramente diferente e podem alterá-la. Confirme, por isso, as regras de carregamento do agente utilizado pela sua equipa.
Um exemplo de estrutura para um repositório com três serviços
repo/
AGENTS.md rules true everywhere, plus the map
apps/web/AGENTS.md TypeScript client, Vite, Vitest
services/worker/AGENTS.md Python queue consumer, pytest
infra/AGENTS.md Terraform and the deploy scriptsO ficheiro raiz é curto de propósito. Indica onde procurar e contém apenas as regras que se aplicam a todos os diretórios.
# AGENTS.md
This is a monorepo. Each top-level directory ships its own AGENTS.md.
Read this file and the AGENTS.md nearest the code you are editing
before you change anything.
- `apps/web` browser client
- `services/worker` queue consumer
- `infra` Terraform and deploy scripts
## Rules for the whole repository
- The package manager is `pnpm`. `npm install` writes a second lockfile
that CI ignores, so the install you tested is not the install that ships.
- Any `generated/` directory is build output. Edit the schema in
`schemas/` and run `pnpm codegen` instead.
- `.env.local` holds real credentials. Do not read it and do not print it.
- If you change code in a directory, update that directory's AGENTS.md
in the same commit.O ficheiro específico do diretório é onde ficam os detalhes. O seu tamanho pode corresponder à complexidade do diretório.
# apps/web
Browser client. Vite and React, TypeScript with `strict` on.
## Commands
- `pnpm dev` serves on port 5173.
- `pnpm test` runs Vitest once and exits.
- `pnpm typecheck` runs `tsc --noEmit`.
## Conventions
- One component per file under `src/components/`.
- All HTTP goes through `src/api/client.ts`. Do not call `fetch` directly,
because the client attaches the auth header and retries on 429.
## Traps
- `pnpm build` does not type check. Vite strips the types instead of
checking them, so a broken type still produces a green build.
Run `pnpm typecheck` as a separate step.O ficheiro do worker tem a mesma estrutura, mas conteúdo diferente: o comando de instalação, pytest -q, o motivo pelo qual o consumer tem de permanecer idempotente e a migração que tem de ser executada antes de os testes passarem. O ficheiro de infraestrutura é onde define as regras que impedem um agente de causar danos. Nunca execute terraform apply. Execute terraform plan e pare aí. Indique também o backend de estado que já está configurado, para que o agente não tente inicializar outro.
Repare no que não aparece em nenhum destes ficheiros: uma descrição da finalidade de cada serviço. Essa informação destina-se às pessoas. O projeto upstream estabelece a mesma distinção ao dizer que "os ficheiros README.md destinam-se às pessoas: guias rápidos, descrições de projetos e orientações para contribuições", enquanto o AGENTS.md contém "o contexto adicional, por vezes detalhado, de que os agentes de programação precisam: etapas de compilação, testes e convenções". A separação entre AGENTS.md e um README destinado às pessoas analisa essa fronteira frase a frase, e um DESIGN.md que regista o motivo pelo qual o código tem esta estrutura aborda o terceiro ficheiro, que explica decisões em vez de comandos.
Quem atualiza o ficheiro quando o código muda?
Há uma regra, e ela fica no ficheiro da raiz: quem altera código num diretório atualiza o AGENTS.md desse diretório no mesmo commit.
Isto funciona por uma razão mecânica, não cultural. O ficheiro específico do diretório fica no mesmo diff que o código, por isso o revisor do pull request vê ambos ao mesmo tempo. Um ficheiro na raiz pertence a toda a gente, o que significa que não pertence a ninguém, e nunca aparece no diff que alguém já está a analisar.
Aplique a regra com uma verificação no pull request. Ela encontra o AGENTS.md mais próximo acima de cada ficheiro alterado e assinala quando esse ficheiro não foi alterado.
#!/usr/bin/env bash
# Warn when code changed but the nearest AGENTS.md above it did not.
changed=$(git diff --name-only origin/main...HEAD)
nearest_doc() {
d=$(dirname "$1")
while [ "$d" != "." ]; do
if [ -f "$d/AGENTS.md" ]; then echo "$d/AGENTS.md"; return; fi
d=$(dirname "$d")
done
echo "AGENTS.md"
}
printf '%s\n' "$changed" | while read -r f; do
[ -n "$f" ] || continue
case "$f" in AGENTS.md|*/AGENTS.md) continue ;; esac
doc=$(nearest_doc "$f")
printf '%s\n' "$changed" | grep -Fqx "$doc" && continue
echo "note: $f changed but $doc was not updated"
doneNuma branch que reformulou o cliente da API sem alterar a documentação, o resultado é semelhante a este:
note: apps/web/src/api/client.ts changed but apps/web/AGENTS.md was not updatedMantenha isto como um aviso, não como uma falha. Um bloqueio rígido ensina as pessoas a adicionar uma linha em branco ao ficheiro para que o CI fique verde, e um ficheiro editado apenas para satisfazer um robô vale menos do que nenhum ficheiro. O aviso dá ao revisor uma pergunta para fazer. É essa parte que funciona de facto.
Como identificar um AGENTS.md desatualizado?
Há duas verificações que pode executar hoje e um sintoma que verá durante uma sessão.
Compare a data de cada ficheiro com a data do código que ele descreve. %cs mostra a data do commit como YYYY-MM-DD.
for f in $(git ls-files '*AGENTS.md'); do
d=$(dirname "$f")
printf '%s doc:%s code:%s\n' "$f" \
"$(git log -1 --format=%cs -- "$f")" \
"$(git log -1 --format=%cs -- "$d")"
doneapps/web/AGENTS.md doc:2026-02-11 code:2026-08-07
services/worker/AGENTS.md doc:2026-07-29 code:2026-08-09
infra/AGENTS.md doc:2026-08-01 code:2026-08-01Uma documentação com data seis meses anterior à data do código não prova que o ficheiro está errado. Indica qual ficheiro deve ler primeiro. É tudo o que precisa de uma verificação que demora um segundo.
Procure caminhos que já não existem. A documentação degrada-se de uma forma muito específica: continua a descrever código que foi eliminado. Todos os caminhos nestes ficheiros estão escritos entre backticks, por isso é fácil extraí-los e testá-los.
grep -o '`[^`]*`' apps/web/AGENTS.md | tr -d '`' | grep '/' | while read -r p; do
[ -e "$p" ] || [ -e "apps/web/$p" ] || echo "missing: $p"
doneLeia o resultado em vez de integrar esta verificação no CI. Ela também sinaliza globs, como src/**/*.ts, e qualquer URL que tenha colocado entre aspas, porque ambos contêm uma barra e nenhum corresponde a um ficheiro no disco.
O sintoma durante uma sessão. O agente lê o ficheiro, tenta abrir src/api/client.ts porque o ficheiro lhe indicou esse caminho e a ferramenta devolve:
No such file or directoryPor isso, faz o que seria razoável e escreve o seu próprio wrapper fetch. Esse é o verdadeiro custo de um ficheiro desatualizado. O agente não ignora a sua documentação. Segue a documentação, chega a um caminho que foi eliminado há três meses e recria código que já existe. Uma skill como Ponytail, que obriga o agente a aplicar a menor alteração que funciona, torna esse impulso de recriação menos frequente, mas não consegue encontrar um helper para o qual o ficheiro apontou o caminho errado.
O Claude Code lê ficheiros AGENTS.md?
Não, e é importante deixar isso claro porque a estrutura aninhada depende desse comportamento. Em agosto de 2026, a documentação afirma: "O Claude Code lê CLAUDE.md, não AGENTS.md." O padrão continua a funcionar; só precisa de um CLAUDE.md junto de cada AGENTS.md.
A forma de importação é adequada quando pretende adicionar linhas específicas da ferramenta às linhas partilhadas. Coloque isto em services/worker/CLAUDE.md:
@AGENTS.md
## Claude Code
Use plan mode for changes under `services/worker/migrations/`.A forma com symlink é adequada quando não há configurações específicas da ferramenta para adicionar.
git ls-files '*AGENTS.md' | while read -r f; do
ln -s AGENTS.md "$(dirname "$f")/CLAUDE.md"
done
ls -l apps/web/CLAUDE.mdln não imprime nada quando é executado com sucesso, por isso verifique a listagem: apps/web/CLAUDE.md -> AGENTS.md. Em seguida, inicie uma sessão e execute /context; os ficheiros carregados aparecem em Ficheiros de memória. No Windows, um symlink requer privilégios de Administrator ou o Developer Mode; nesse caso, use a importação @AGENTS.md.
Há uma armadilha relacionada com isto. Depois de /compact, o ficheiro raiz é lido novamente do disco, mas os ficheiros aninhados nos subdiretórios não são reinjetados. Eles voltam a ser carregados na próxima vez que o agente ler um ficheiro desse diretório. Se uma regra por diretório parecer deixar de ser aplicada a meio de uma sessão longa, normalmente essa é a causa. Tocar em qualquer ficheiro do diretório faz com que a regra seja carregada novamente.
Definições que encaminham outros agentes para AGENTS.md
O Codex lê AGENTS.md nativamente. Em cada nível, verifica primeiro AGENTS.override.md, o que permite definir uma substituição local para um diretório sem editar o ficheiro partilhado. Para de fundir ficheiros quando o tamanho combinado atinge 32 KiB, o valor predefinido de project_doc_max_bytes. Esta é mais uma razão para manter o ficheiro raiz pequeno.
O Aider recebe essa configuração através de .aider.conf.yml, com a linha read: AGENTS.md.
O Gemini CLI recebe essa configuração através de .gemini/settings.json, com { "context": { "fileName": "AGENTS.md" } }.
A documentação upstream descreve uma renomeação compatível com versões anteriores para repositórios que ainda usam o nome singular mais antigo: mv AGENT.md AGENTS.md && ln -s AGENTS.md AGENT.md.
Num monorepo muito grande, a definição claudeMdExcludes do Claude Code ignora ficheiros antecessores por caminho ou glob. Isto é útil quando o diretório de outra equipa fica acima do seu.
Como isto é diferente da memória do agente ou de uma skill?
Estes mecanismos parecem semelhantes, mas falham de formas completamente diferentes. Por isso, é importante identificar exatamente qual deles deve ser usado.
AGENTS.md é escrito por si, submetido ao git, revisto num pull request e igual para todas as pessoas que clonam o repositório. A memória do agente é escrita pelo agente, armazenada fora do repositório e local a uma máquina. A documentação do Claude Code estabelece a mesma distinção: CLAUDE.md contém "Instruções e regras" que escreve, a memória automática contém "Aprendizagens e padrões" que o Claude escreve, e o diretório de memória não é partilhado entre máquinas. O teste é simples. Se um facto tiver de ser verdadeiro para um colega que faz um clone limpo, não pode ficar na memória. Como a memória do agente persiste entre sessões aborda essa parte.
Uma skill é a terceira opção. AGENTS.md é contexto carregado em todas as sessões; uma skill é um procedimento carregado quando é necessário. A documentação do Claude Code apresenta uma regra útil: "Se uma entrada for um procedimento com várias etapas ou só for relevante para uma parte da base de código, mova-a para uma skill ou para uma regra com âmbito de caminho." A segunda parte dessa frase corresponde exatamente ao que um AGENTS.md aninhado resolve. A primeira parte é o objetivo das skills de agente e, quando o mesmo procedimento for necessário em mais do que um repositório, partilhe a skill entre repositórios em vez de copiar os mesmos parágrafos para dez ficheiros AGENTS.md diferentes.
O projeto upstream indica que "à data de redação, o repositório principal da OpenAI tem 88 ficheiros AGENTS.md". Esse número demonstra o princípio. Um repositório grande não precisa de um ficheiro maior. Precisa de mais ficheiros pequenos, cada um junto do código que descreve e sob responsabilidade de quem alterou esse código pela última vez.
FAQ
Um AGENTS.md aninhado substitui o ficheiro raiz ou é acrescentado a ele?
É acrescentado. A documentação upstream diz que "o ficheiro mais próximo tem precedência", mas isso descreve o que acontece num conflito, não o que é carregado. O Codex "concatena os ficheiros desde a raiz para baixo, juntando-os com linhas em branco", e o Claude Code concatena todos os ficheiros encontrados ao subir a partir do diretório de trabalho, em vez de os substituir. O ficheiro mais próximo só prevalece quando dois ficheiros dão instruções diferentes sobre o mesmo assunto. Escreva as regras partilhadas uma vez no ficheiro raiz e não as repita em todos os diretórios.
Qual deve ser o tamanho do AGENTS.md raiz?
Deve ser suficientemente pequeno para não se importar que seja incluído no início de todos os pedidos que fizer nesse repositório, porque é isso que acontece. A documentação do Claude Code recomenda menos de 200 linhas por ficheiro e avisa que ficheiros maiores "reduzem a adesão". Por predefinição, o Codex deixa de juntar ficheiros de instruções quando o total chega a 32 KiB. Se o ficheiro raiz documentar quatro serviços, a maior parte será informação irrelevante para qualquer tarefa individual. Mova os detalhes para ficheiros específicos de cada diretório e deixe um mapa no ficheiro raiz.
Como impeço que estes ficheiros fiquem desatualizados?
Inclua uma regra no ficheiro raiz: quem alterar código num diretório atualiza o AGENTS.md desse diretório no mesmo commit. Colocar o ficheiro junto do código faz com que a regra seja cumprida, porque a alteração aparece no mesmo diff do pull request que uma pessoa já está a rever. Adicione um aviso de CI que associe cada caminho alterado ao AGENTS.md mais próximo acima dele e, periodicamente, compare git log -1 --format=%cs em cada ficheiro com o mesmo comando executado no diretório que esse ficheiro documenta.
O Claude Code lê ficheiros AGENTS.md?
Não. Em agosto de 2026, a documentação afirma: "O Claude Code lê CLAUDE.md, não AGENTS.md." Crie um CLAUDE.md no mesmo diretório, com @AGENTS.md na primeira linha. Isso carrega o ficheiro partilhado e permite adicionar instruções específicas do Claude abaixo dele. Um symlink criado com ln -s AGENTS.md CLAUDE.md funciona quando não há nada adicional a incluir, mas no Windows requer direitos de Administrator ou Developer Mode. Execute /context numa sessão e confirme que o ficheiro aparece em Memory files.
Onde coloco uma regra que só se aplica ocasionalmente?
Não a coloque em AGENTS.md. Esse ficheiro é carregado em todas as sessões, por isso cada linha compete pela atenção com o pedido que escreveu. Um procedimento com vários passos, necessário apenas ocasionalmente, pertence a uma skill, que é carregada a pedido. Uma regra aplicável a um único diretório pertence ao AGENTS.md desse diretório. Um facto que o agente pode obter diretamente do código, como a árvore de diretórios ou a lista de dependências, não pertence a nenhum dos dois.