Saltar al contenido
< samuelsantana.dev />
Volver al BlogLista Memory files do /context: dois arquivos CLAUDE.md carregados dentro da janela de contexto e o AGENTS.md riscado, do lado de fora, não carregado.

Engenharia de contexto na prática: o que o agente não carrega não existe

Estás leyendo la versión en portugués de este artículo. Léelo en Español

Samuel Santana
Publicado el 20 de septiembre de 2026
IASoftware Architecture

Em 19 de agosto eu descobri que as regras escritas para todos os meus repositórios nunca tinham valido em sessão nenhuma. Elas estavam num AGENTS.md na pasta que reúne os projetos, com o título "Codex Custom Instructions for Developers". Eu não uso Codex. Uso Claude Code, e o Claude Code daquela data lia CLAUDE.md. O arquivo estava no disco, bem escrito, e fora do contexto de todas as sessões.

A Anthropic define engenharia de contexto como o conjunto de estratégias para curar e manter o conjunto ótimo de tokens durante a inferência do modelo, "including all the other information that may land there outside of the prompts" (Effective context engineering for AI agents, setembro de 2025). O post de julho sobre tokens, contexto, skills e agentes apresenta as peças. Este é sobre o que eu faço com elas e, principalmente, sobre o que quebrou.

Tudo aqui aconteceu no Claude Code, entre julho e setembro de 2026, e cada mecanismo citado vem da documentação ou do changelog da época.

O que não foi carregado não existe

A página de memória do Claude Code, na versão arquivada de 18/08, resolvia a questão em uma frase: "Claude Code reads CLAUDE.md, not AGENTS.md." E recomendava, para quem já tinha um AGENTS.md de outros agentes, criar um CLAUDE.md que o importasse.

O curioso é que eu tinha lido o contrário em outra documentação oficial. O guia de agentes que vem dentro do pacote do Next.js 16.2.10, em node_modules/next/dist/docs/01-app/02-guides/ai-agents.md, afirmava: "Most AI coding agents — including Claude Code, Cursor, GitHub Copilot, and others — automatically read AGENTS.md when they start a session" (o arquivo, direto do pacote publicado). A mesma página, algumas linhas abaixo, explicava que o create-next-app gera também um CLAUDE.md com @AGENTS.md, para que usuários do Claude Code recebam as mesmas instruções. Se o Claude Code lesse o AGENTS.md sozinho, o import seria desnecessário. O import era a parte certa; a frase, não.

Nos projetos Next.js isso nunca me mordeu, porque desde 07/07 o .claude/CLAUDE.md do vertex-web importava o arquivo:

# Vertex Web - System Context & AI Agent Rules

@../AGENTS.md

O AGENTS.md da pasta-mãe não tinha ninguém importando. Em 19/08 ele virou D:\github\CLAUDE.md. Como o Claude Code carrega CLAUDE.md do diretório de trabalho e de todos os diretórios acima dele, um arquivo na pasta-mãe entra em toda sessão aberta dentro de qualquer repositório.

Há dois dias, em 18/09, a versão 2.1.277 passou a ler AGENTS.md: "in a project with no CLAUDE.md, Claude Code reads AGENTS.md instead". A condição é o que importa. Pela documentação daquele mesmo dia, se existe um CLAUDE.md, .claude/CLAUDE.md ou CLAUDE.local.md no diretório de trabalho ou acima dele, o Claude Code lê esses arquivos e "ignores every AGENTS.md". Para ler os dois, é preciso mudar a opção Project instructions para claude-md-and-agents-md. As sessões que eu abria no vertex-web, que tinha o próprio CLAUDE.md, continuariam sem aquele AGENTS.md mesmo na versão de anteontem. E hoje, com um CLAUDE.md na própria pasta-mãe, todo AGENTS.md dentro dela é ignorado na configuração padrão.

A lição não é "use o nome de arquivo certo". É trocar a pergunta "qual arquivo esta ferramenta lê?", cuja resposta depende de versão, configuração e de qual documentação você leu, por "quais arquivos entraram nesta sessão?". A documentação já dava o comando em agosto: /context, na lista Memory files. Se o arquivo não aparece ali, o modelo não o vê.

Carregar não é sobrescrever

Há um segundo detalhe na mesma página, e ele muda como se escreve um arquivo de instruções em mais de um nível. No Claude Code, os CLAUDE.md encontrados "are concatenated into context rather than overriding each other", da raiz do sistema de arquivos até o diretório de trabalho. A convenção do agents.md diz outra coisa: em monorepos, "the closest one takes precedence". São semânticas diferentes para arquivos parecidos.

Na concatenação, os dois arquivos chegam ao modelo, e a documentação avisa o que acontece quando eles discordam: "if two rules contradict each other, Claude may pick one arbitrarily". Por isso a precedência que eu quero está escrita, nas duas pontas. No arquivo da pasta-mãe:

- Cada repo tem seu próprio `CLAUDE.md` com as convenções específicas — **ele vence este arquivo**
  em caso de conflito.

E no topo do arquivo do repositório:

> `D:\github\CLAUDE.md` applies too. Where the two disagree, this file wins.

É rudimentar: o modelo lê os dois, e o próprio texto diz qual vence. Melhor seria não haver contradição, e a documentação recomenda revisar os arquivos periodicamente; até a próxima revisão, a regra escrita é o que existe.

Um documento que existe também pode não existir

Em 21/08, uma sessão que trabalhava no prerender do blog justificou uma escolha com o argumento "senão o Google indexa o preview". O docs/rendering-strategies.md do próprio repositório já tinha testado e derrubado esse argumento: os previews da Vercel ficam atrás de SSO e respondem com X-Robots-Tag: noindex. O documento estava lá. Só não estava no contexto, porque a pasta docs/ não carrega sozinha; quem carrega é o CLAUDE.md. A nota daquele dia registra o erro do jeito certo: o argumento foi escrito sem ler o documento que o repositório já tinha.

A resposta para isso não é "ler mais os docs". É levar a conclusão para o arquivo que sempre carrega, com o motivo junto. É o que está hoje no CLAUDE.md do vertex-web:

- **Previews are behind Vercel SSO.** `curl` gets a 302 to `vercel.com/sso-api`. [...] They also carry
  `X-Robots-Tag: noindex`, which is why "previews would get indexed" is never a valid argument here.

Esse é o modelo híbrido que o artigo da Anthropic descreve para o próprio Claude Code: "CLAUDE.md files are naively dropped into context up front, while primitives like glob and grep allow it to navigate its environment and retrieve files just-in-time". O que vai na frente é o que o agente não pode deixar de saber; o resto fica a um ponteiro de distância. No meu arranjo, o CLAUDE.md carrega conclusões e regras caras de violar, docs/ carrega a medição e a história, e o CLAUDE.md diz qual documento ler antes de mexer em quê. Uma seção do vertex-web se chama "Rendering — read docs/rendering-strategies.md before touching a route", e logo abaixo vem a versão curta das conclusões, para o caso de ninguém abrir o documento.

Por que não colocar tudo no arquivo que sempre carrega

Porque contexto não é de graça. O mesmo artigo da Anthropic chama o problema de context rot: "As the number of tokens in the context window increases, the model's ability to accurately recall information from that context decreases". O modelo tem um "attention budget", e cada token gasta um pouco dele.

A evidência pública vai na mesma direção. O relatório técnico da Chroma (julho de 2025) testou 18 modelos: o desempenho "varies significantly as input length changes, even on simple tasks", e no LongMemEval todos foram significativamente melhores com o prompt focado do que com o completo. Antes, Lost in the Middle (Liu et al., TACL) mostrou que o desempenho cai quando a informação relevante está no meio de um contexto longo.

A documentação do Claude Code transforma isso em regra prática: mirar menos de 200 linhas por CLAUDE.md, porque "longer files consume more context and reduce adherence". Quebrar o arquivo em @imports organiza, mas não economiza, porque os importados também carregam no início.

Uma ressalva: esses estudos medem entradas longas em geral, não arquivos de instrução, e eu não tenho medição de aderência do meu próprio CLAUDE.md. O que tiro deles é a direção: cada linha do arquivo que sempre carrega disputa atenção com a tarefa.

Regra boa carrega o motivo e a prova

O CLAUDE.md do vertex-web abre com uma seção chamada "Rules that will bite": as regras cuja violação já custou algo, cada uma com o motivo e a evidência. A primeira:

- **Merging does not publish.** Vercel is not set to auto-promote here; after a merge to `main`,
  Samuel promotes the deployment in the dashboard by hand. The Vercel MCP connector cannot do it —
  verified: it lists, inspects, triggers deploys and changes protection, and does not promote.
  "Merged" is not "live".

Outra, na seção de renderização, diz que useCurrentUser() tem três estados e não dois, e termina com o custo de esquecer: "it has happened twice, in the header and nearly again in the comments".

O motivo não é enfeite. A documentação diz que o conteúdo do CLAUDE.md "is delivered as a user message after the system prompt" e que não há "guarantee of strict compliance". Uma regra sem motivo é frágil de dois jeitos: é aplicada como ritual onde não cabe, ou é abandonada quando aparece um argumento plausível. Com o motivo, o modelo consegue dizer se o caso em mãos se encaixa. E eu também, semanas depois.

A documentação pede instruções "concrete enough to verify". O "verified: it lists, inspects..." é isso: diz qual teste foi feito, para que ninguém precise refazê-lo.

E o que precisa valer de qualquer jeito não deveria depender de contexto. O Claude Code trata esses arquivos "as context, not enforced configuration"; para bloquear uma ação independentemente do que o modelo decidir, a documentação indica um hook. Fora do agente vale o mesmo princípio: no vertex-web, segredo em commit é barrado por um hook do git, o secretlint rodando sobre os arquivos staged via husky e lint-staged. Nenhuma instrução faz esse trabalho.

Arquivo de contexto é infraestrutura

Em 19/08 eu decidi que, nos três repositórios ativos, CLAUDE.md e a pasta docs/ passariam a ser gitignored e desrastreados. Instruções de agente e planos ficam no disco, não no GitHub. A decisão teve duas consequências: uma imediata e uma atrasada.

A imediata foram os ponteiros mortos. Num dos repositórios, vinte referências apontavam para arquivos que estavam saindo do público, três delas visíveis no Storybook publicado, incluindo um link para um DESIGN.md no GitHub que daria 404. Cada uma foi reescrita para carregar o próprio raciocínio, e a regra ficou nos três CLAUDE.md: um comentário que diz "see docs/x.md" é ponteiro morto para quem lê o repositório no GitHub; o porquê mora no ponto de uso.

A atrasada apareceu em 21/08: o CLAUDE.md tinha sumido do disco nos três repositórios. Não houve mistério. O git tinha deixado de ser a cópia de segurança desses arquivos, e nada o substituiu. Eu reconstruí o do vertex-web a partir do que era verificável no repositório, e o arquivo novo começa dizendo isso:

> **This file was rebuilt on 21/08/2026 from what is verifiable in the repository.** The original was
> gitignored (PR #79) and then lost from disk — a clone does not restore what git does not track, and
> nothing noticed until a session went looking for it. Anything the old file recorded that is *not*
> derivable from the code is gone; what follows was checked against the source, the build output and
> the CI config, not remembered.

O que não deu para reconstruir é justamente o que mais importa: o que não se deriva do código. A documentação chega lá pelo outro lado: a checagem do /doctor "cuts content Claude can derive from the codebase" e mantém "pitfalls, rationale, and conventions that differ from tool defaults". O conteúdo valioso de um arquivo de contexto é o que não tem outra cópia. Se ele sai do git, precisa de backup em outro lugar; naquele dia, isso ficou registrado como decisão em aberto.

Arquivo de contexto também envelhece

O CLAUDE.md da pasta-mãe dizia que quatro repositórios "ainda têm AGENTS.md — não foram tocados". O git diz outra coisa: em três deles (vertex-api, vela-core e vela-ui), o arquivo foi removido em 19/08, em commits feitos entre 16h43 e 16h45. A frase continuava lá em 07/09, quando uma sessão a carregou exatamente assim. Só foi corrigida em 18/09, e a correção veio com data: "Conferido em 18/09/2026".

A data é a parte que vale copiar. Ela diz a quem lê, pessoa ou modelo, quão velho é o fato. O próprio Claude Code faz isso com a memória automática: grava a hora da escrita num campo modified do frontmatter, porque "the timestamp shows how current the fact is".

A passagem de turno: a nota é hipótese, o sistema é fato

"Each Claude Code session begins with a fresh context window", diz a documentação. Entre uma sessão e outra só atravessa o que está escrito. No meu caso, além dos CLAUDE.md, atravessa um GAPS.md com seções "▶ RETOMAR AQUI", a mais nova no topo. A primeira seção do CLAUDE.md da pasta-mãe manda lê-lo antes de qualquer coisa. Ele é grande demais para entrar inteiro no contexto, e não precisa: a instrução aponta uma seção. É a versão caseira do que o artigo da Anthropic chama de structured note-taking. O esqueleto, simplificado:

## ▶ RETOMAR AQUI — atualizado em 21/08/2026

**Sessão de <frente de trabalho>.** O que foi pedido e o que de fato aconteceu.

### ✅ Entregue
| PR | O quê | Estado (mergeado, publicado, aguardando) |

### 🔍 Premissas que o código derrubou

### ⏳ Pendente, e de quem

Verificado em <data>: <como, com qual comando ou header, não "presumido">

A regra mais útil desse arranjo não está no GAPS.md. Está no CLAUDE.md, logo abaixo da instrução de lê-lo:

**Conferir antes de executar.** Em 19/08 duas pendências listadas como abertas já estavam feitas —
o domínio na Vercel e o domínio verificado no Resend. Um item escrito pela sessão anterior é uma
hipótese sobre o passado, não o estado atual.

Dois dias depois, o mesmo padrão veio de um plano. O código derrubou as duas premissas do plano de prerender do blog: tirar o cookies() não tornou a rota do post estática, porque faltava generateStaticParams, e o painel admin, que "não precisava de tradução", era traduzido de propósito nos três idiomas. A nota do dia resumiu: "um plano escrito à noite é hipótese sobre o passado, e o repo vence".

O que tornou as notas mais úteis foi dizer como cada coisa foi verificada: "verificado no log de build da Vercel e por header HTTP, não pela tabela de rotas local". Uma afirmação que diz como foi conferida pode ser conferida de novo em segundos.

Memória, skills e ferramentas: o que entra só quando precisa

A memória automática do Claude Code guarda um índice, o MEMORY.md, do qual as primeiras 200 linhas (ou 25 KB) entram em toda sessão, e um arquivo por memória, lido sob demanda. São quatro tipos, user, feedback, project e reference, registrados no frontmatter. Nas minhas, as de correção e de projeto carregam o fato e mais duas linhas: Why e How to apply.

Um exemplo de 07/09. Quando autorizo uma ação arriscada, costumo pôr junto uma condição de conferência; naquele dia foi "confira no plan que cria só a role e a policy". O plano do Terraform mostrava duas criações e duas alterações, e uma das alterações extras reverteria uma variável de produção para um valor de exemplo, o que derrubaria o acesso de administrador do site. A sessão parou e reportou. A memória que ficou registra a regra, o caso e o que fazer quando a condição falha: não completar a ação, nem "só a parte segura" sem avisar. Sem o why, a próxima sessão trataria a condição como uma caixinha a marcar.

Procedimentos vão para skills. A do vertex-web, .claude/skills/verify/SKILL.md, existe desde 10/07: como subir o site com a vertex-api e o Postgres, as pegadinhas da carga de dados e uma armadilha que me custou tempo, a de que procurar o texto de uma mensagem na resposta dá falso positivo, porque toda página embute o catálogo inteiro do next-intl no payload RSC. É receita, não fato para toda sessão, e a documentação traça a mesma linha: se uma entrada "is a multi-step procedure or only matters for one part of the codebase, move it to a skill or a path-scoped rule instead". Até ser usada, a skill ocupa no contexto só o nome e a descrição, o que a Anthropic chama de progressive disclosure.

Ferramentas também são contexto. Duas vezes, um conector MCP adicionado à conta no meio de uma sessão não apareceu nela, só na seguinte: o do Resend em agosto e o da AWS em 31/08. A documentação não trata desse caso explicitamente, então trato como comportamento observado, não como regra do produto. Mesmo assim ele virou linha no CLAUDE.md, e a sessão começa conferindo quais ferramentas carregaram.

E há o que nunca deve entrar, porque tudo o que passa pelo contexto fica na transcrição. Connection string e API key nunca são coladas no chat nem impressas em saída de ferramenta, e criar uma chave de API pelo conector está proibido: o retorno da ferramenta traria o segredo junto.

Como eu confiro hoje

Nada disso exige ferramenta nova, só conferir em vez de supor:

  • No começo da sessão, /context: todo arquivo que eu espero está em Memory files? Se o projeto depende de AGENTS.md, olhar também Project instructions em /config.
  • Instrução ignorada: o arquivo carregou? Outra instrução a contradiz? Ela é específica o bastante para ser verificada?
  • Ponteiros mortos em cada repositório público, antes de abrir PR:
# Arquivos rastreados que citam documentos que não estão no GitHub
git grep -nE "see docs/|CLAUDE\.md|AGENTS\.md" -- . ':!*.md' ':!.gitignore'
  • Toda afirmação de nota com data e método: "conferido em", com o comando ou o header que provou.
  • O que precisa valer sempre vai para hook ou CI, não para o CLAUDE.md.

Por que isso importa

O modelo só segue o que vê, e vê como mensagem de usuário, sem garantia de obediência. Por isso engenharia de contexto, no dia a dia, é menos sobre a prosa do prompt e mais sobre operação: o que carrega, em que ordem, quem vence quando há conflito, o que fica fora até ser necessário, o que nunca pode entrar e quão velho é cada fato.

Nenhuma das falhas que contei foi sobre capacidade do modelo: um arquivo que não carregou, um documento que não foi consultado, um arquivo que sumiu do disco, uma frase que envelheceu e pendências que já estavam resolvidas. Todas eram sobre contexto, e todas teriam sido pegas por alguém que conferisse em vez de supor. Esse alguém pode ser o próprio agente, desde que o contexto mande conferir.

Referências

Comentarios

Cargando comentarios...