Saltar al contenido
< samuelsantana.dev />
Volver al BlogLista Memory files de /context: dos archivos CLAUDE.md cargados dentro de la ventana de contexto y AGENTS.md tachado, afuera, no cargado.

Ingeniería de contexto en la práctica: lo que el agente no carga no existe

Samuel Santana
Publicado el 20 de septiembre de 2026
IASoftware Architecture

El 19 de agosto descubrí que las reglas que había escrito para todos mis repositorios nunca habían valido en ninguna sesión. Estaban en un AGENTS.md en la carpeta que reúne mis proyectos, con el título "Codex Custom Instructions for Developers". Yo no uso Codex. Uso Claude Code, y el Claude Code de esa fecha leía CLAUDE.md. El archivo estaba en el disco, bien escrito, y fuera del contexto de todas las sesiones.

Anthropic define la ingeniería de contexto como el conjunto de estrategias para curar y mantener el conjunto óptimo de tokens durante la inferencia del modelo, "including all the other information that may land there outside of the prompts" (Effective context engineering for AI agents, septiembre de 2025). El post de julio sobre tokens, contexto, skills y agentes presenta las piezas. Este trata de lo que hago con ellas y, sobre todo, de lo que se rompió.

Todo lo que cuento pasó en Claude Code entre julio y septiembre de 2026, y cada mecanismo que cito viene de la documentación o del changelog de la época.

Lo que no se cargó no existe

La página de memoria de Claude Code, en la versión archivada del 18 de agosto, resolvía la cuestión en una frase: "Claude Code reads CLAUDE.md, not AGENTS.md." Y recomendaba, a quien ya tuviera un AGENTS.md para otros agentes, crear un CLAUDE.md que lo importara.

Lo curioso es que yo había leído lo contrario en otra documentación oficial. La guía de agentes que viene dentro del paquete de Next.js 16.2.10, en node_modules/next/dist/docs/01-app/02-guides/ai-agents.md, afirmaba: "Most AI coding agents — including Claude Code, Cursor, GitHub Copilot, and others — automatically read AGENTS.md when they start a session" (el archivo, directo del paquete publicado). Unas líneas más abajo, la misma página explicaba que create-next-app también genera un CLAUDE.md con @AGENTS.md, para que los usuarios de Claude Code reciban las mismas instrucciones. Si Claude Code leyera AGENTS.md por su cuenta, el import sobraría. El import era lo correcto; la frase, no.

En mis proyectos Next.js esto nunca me mordió, porque desde el 7 de julio el .claude/CLAUDE.md de vertex-web importaba el archivo:

# Vertex Web - System Context & AI Agent Rules

@../AGENTS.md

El AGENTS.md de la carpeta madre no lo importaba nadie. El 19 de agosto pasó a ser D:\github\CLAUDE.md. Como Claude Code carga CLAUDE.md desde el directorio de trabajo y desde todos los directorios superiores, un archivo en la carpeta madre entra en cada sesión abierta dentro de cualquier repositorio.

Hace dos días, el 18 de septiembre, la versión 2.1.277 empezó a leer AGENTS.md: "in a project with no CLAUDE.md, Claude Code reads AGENTS.md instead". Lo que importa es la condición. Según la documentación de ese mismo día, si hay un CLAUDE.md, .claude/CLAUDE.md o CLAUDE.local.md en el directorio de trabajo o por encima, Claude Code lee esos archivos e "ignores every AGENTS.md". Para leer ambos hay que cambiar la opción Project instructions a claude-md-and-agents-md. Las sesiones que yo abría en vertex-web, que tenía su propio CLAUDE.md, habrían seguido sin aquel AGENTS.md incluso con la versión de anteayer. Y hoy, con un CLAUDE.md en la propia carpeta madre, todo AGENTS.md dentro de ella se ignora con la configuración por defecto.

La lección no es "usa el nombre de archivo correcto". Es cambiar la pregunta "¿qué archivo lee esta herramienta?", cuya respuesta depende de la versión, de la configuración y de qué documentación leíste, por "¿qué archivos entraron en esta sesión?". La documentación ya daba el comando en agosto: /context, en la lista Memory files. Si el archivo no aparece ahí, el modelo no lo ve.

Cargar no es sobrescribir

La misma página tiene un segundo detalle, y cambia cómo se escribe un archivo de instrucciones en más de un nivel. En Claude Code, los CLAUDE.md encontrados "are concatenated into context rather than overriding each other", desde la raíz del sistema de archivos hasta el directorio de trabajo. La convención de agents.md dice otra cosa: en monorepos, "the closest one takes precedence". Archivos parecidos, semánticas distintas.

Con la concatenación, ambos archivos llegan al modelo, y la documentación avisa qué pasa cuando discrepan: "if two rules contradict each other, Claude may pick one arbitrarily". Por eso la precedencia que quiero está escrita, en los dos extremos. El archivo de la carpeta madre está en portugués; esta línea dice que el CLAUDE.md de cada repositorio gana en caso de conflicto:

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

Y al principio del archivo del repositorio:

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

Es rudimentario: el modelo lee ambos y el propio texto dice cuál gana. Mejor sería no tener contradicciones, y la documentación recomienda revisar los archivos periódicamente; hasta la próxima revisión, la regla escrita es lo que hay.

Un documento que existe también puede no existir

El 21 de agosto, una sesión que trabajaba en el prerender del blog justificó una decisión con el argumento "si no, Google indexa el preview". El docs/rendering-strategies.md del propio repositorio ya había probado y descartado ese argumento: los previews de Vercel están detrás de SSO y responden con X-Robots-Tag: noindex. El documento estaba ahí. Solo que no estaba en el contexto, porque la carpeta docs/ no se carga sola; lo que se carga es el CLAUDE.md. Mis notas de ese día registran el error como corresponde: el argumento se escribió sin leer el documento que el repositorio ya tenía.

La respuesta no es "leer más los docs". Es llevar la conclusión al archivo que siempre se carga, con el motivo incluido. Es lo que dice hoy el CLAUDE.md de 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.

Es el modelo híbrido que el artículo de Anthropic describe para el propio 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". Lo que va al principio es lo que el agente no puede dejar de saber; el resto queda a un puntero de distancia. En mi esquema, el CLAUDE.md lleva conclusiones y reglas caras de romper, docs/ lleva las mediciones y la historia, y el CLAUDE.md dice qué documento leer antes de tocar qué. Una sección de vertex-web se titula "Rendering — read docs/rendering-strategies.md before touching a route", y justo debajo viene la versión corta de las conclusiones, por si nadie abre el documento.

Por qué no poner todo en el archivo que siempre se carga

Porque el contexto no es gratis. El mismo artículo de Anthropic llama al problema context rot: "As the number of tokens in the context window increases, the model's ability to accurately recall information from that context decreases". El modelo tiene un "attention budget", y cada token gasta un poco de él.

La evidencia pública apunta en la misma dirección. El informe técnico de Chroma (julio de 2025) probó 18 modelos: el rendimiento "varies significantly as input length changes, even on simple tasks", y en LongMemEval todos rindieron significativamente mejor con el prompt enfocado que con el completo. Antes, Lost in the Middle (Liu et al., TACL) mostró que el rendimiento cae cuando la información relevante está en medio de un contexto largo.

La documentación de Claude Code lo convierte en una regla práctica: apuntar a menos de 200 líneas por CLAUDE.md, porque "longer files consume more context and reduce adherence". Dividir el archivo en @imports lo ordena, pero no ahorra nada, porque los archivos importados también se cargan al inicio.

Una salvedad: esos estudios miden entradas largas en general, no archivos de instrucciones, y no tengo una medición de adherencia de mi propio CLAUDE.md. Lo que saco de ellos es la dirección: cada línea del archivo que siempre se carga compite por atención con la tarea.

Una buena regla lleva su motivo y su prueba

El CLAUDE.md de vertex-web empieza con una sección llamada "Rules that will bite": las reglas cuya violación ya costó algo, cada una con su motivo y su evidencia. La primera:

- **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".

Otra, en la sección de renderizado, dice que useCurrentUser() tiene tres estados y no dos, y termina con el costo de olvidarlo: "it has happened twice, in the header and nearly again in the comments".

El motivo no es adorno. La documentación dice que el contenido del CLAUDE.md "is delivered as a user message after the system prompt" y que no hay "guarantee of strict compliance". Una regla sin motivo es frágil de dos maneras: se aplica como ritual donde no corresponde, o se abandona en cuanto aparece un argumento plausible. Con el motivo, el modelo puede decidir si el caso encaja. Y yo también, semanas después.

La documentación pide instrucciones "concrete enough to verify". El "verified: it lists, inspects..." es exactamente eso: dice qué prueba se hizo, para que nadie tenga que repetirla.

Y lo que tiene que cumplirse sí o sí no debería depender del contexto. Claude Code trata estos archivos "as context, not enforced configuration"; para bloquear una acción decida lo que decida el modelo, la documentación indica un hook. Fuera del agente vale el mismo principio: en vertex-web, un secreto en un commit lo frena un hook de git, secretlint sobre los archivos en staging mediante husky y lint-staged. Ninguna instrucción hace ese trabajo.

Los archivos de contexto son infraestructura

El 19 de agosto decidí que, en los tres repositorios activos, CLAUDE.md y la carpeta docs/ pasarían a estar en el gitignore y fuera del seguimiento de git. Las instrucciones de agente y los planes viven en el disco, no en GitHub. La decisión tuvo dos consecuencias: una inmediata y otra tardía.

La inmediata fueron los punteros muertos. En uno de los repositorios, veinte referencias apuntaban a archivos que dejaban de ser públicos, tres de ellas visibles en el Storybook publicado, incluido un enlace a un DESIGN.md en GitHub que habría dado 404. Cada una se reescribió para llevar su propio razonamiento, y la regla quedó en los tres CLAUDE.md: un comentario que dice "see docs/x.md" es un puntero muerto para quien lee el repositorio en GitHub; el porqué vive en el punto de uso.

La tardía apareció el 21 de agosto: el CLAUDE.md había desaparecido del disco en los tres repositorios. No hubo misterio. Git había dejado de ser la copia de seguridad de esos archivos y nada lo reemplazó. Reconstruí el de vertex-web a partir de lo que era verificable en el repositorio, y el archivo nuevo empieza diciéndolo:

> **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.

Lo que no se pudo reconstruir es justo lo que más importa: lo que no se deriva del código. La documentación llega ahí por el otro lado: la revisión de /doctor "cuts content Claude can derive from the codebase" y conserva "pitfalls, rationale, and conventions that differ from tool defaults". El contenido valioso de un archivo de contexto es el que no tiene otra copia. Si sale de git, necesita una copia de seguridad en otro lugar; ese día, quedó registrado como decisión pendiente.

Los archivos de contexto también envejecen

El CLAUDE.md de la carpeta madre decía que cuatro repositorios "ainda têm AGENTS.md — não foram tocados" (todavía tienen AGENTS.md, sin tocar). Git dice otra cosa: en tres de ellos (vertex-api, vela-core y vela-ui), el archivo se eliminó el 19 de agosto, en commits hechos entre las 16:43 y las 16:45, hora de Brasilia. La frase seguía ahí el 7 de septiembre, cuando una sesión la cargó tal cual. Solo se corrigió el 18 de septiembre, y la corrección llegó con fecha: "Conferido em 18/09/2026" (verificado el 18/09/2026).

La fecha es la parte que vale la pena copiar. Le dice a quien lee, persona o modelo, cuán viejo es el dato. El propio Claude Code lo hace con la memoria automática: guarda la hora de escritura en un campo modified del frontmatter, porque "the timestamp shows how current the fact is".

El relevo entre sesiones: la nota es una hipótesis, el sistema es el hecho

"Each Claude Code session begins with a fresh context window", dice la documentación. Entre una sesión y otra solo cruza lo que está escrito. En mi caso, además de los CLAUDE.md, cruza un GAPS.md con secciones "▶ RETOMAR AQUI" ("retomar aquí"), la más nueva arriba. La primera sección del CLAUDE.md de la carpeta madre manda leerlo antes que nada. Es demasiado grande para entrar entero en el contexto, y no hace falta: la instrucción apunta a una sección. Es la versión casera de lo que el artículo de Anthropic llama structured note-taking. El esqueleto, simplificado y traducido:

## ▶ RETOMAR AQUI — actualizado el 21/08/2026

**Sesión de <frente de trabajo>.** Qué se pidió y qué pasó de verdad.

### ✅ Entregado
| PR | Qué | Estado (mergeado, publicado, esperando) |

### 🔍 Supuestos que el código desmintió

### ⏳ Pendiente, y de quién

Verificado el <fecha>: <cómo, con qué comando o header, no "supuesto">

La regla más útil de este esquema no está en el GAPS.md. Está en el CLAUDE.md, justo debajo de la instrucción de leerlo. En el original, en portugués, dice: verificar antes de ejecutar; el 19 de agosto, dos pendientes listados como abiertos ya estaban hechos (el dominio en Vercel y el dominio verificado en Resend); un ítem escrito por la sesión anterior es una hipótesis sobre el pasado, no el estado actual.

**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.

Dos días después, el mismo patrón vino de un plan. El código desmintió las dos premisas del plan de prerender del blog: quitar el cookies() no volvió estática la ruta del post, porque faltaba generateStaticParams, y el panel de administración, que "no necesitaba traducción", estaba traducido a propósito en los tres idiomas. La nota del día lo resumió: "un plan escrito de noche es una hipótesis sobre el pasado, y el repo gana".

Lo que hizo más útiles las notas con el tiempo fue decir cómo se verificó cada cosa: "verificado en el log de build de Vercel y por header HTTP, no por la tabla de rutas local". Una afirmación que dice cómo se comprobó se puede volver a comprobar en segundos.

Memoria, skills y herramientas: lo que entra solo cuando hace falta

La memoria automática de Claude Code guarda un índice, MEMORY.md, cuyas primeras 200 líneas (o 25 KB) entran en cada sesión, y un archivo por memoria, que se lee bajo demanda. Hay cuatro tipos, user, feedback, project y reference, registrados en el frontmatter. En las mías, las de corrección y de proyecto llevan el dato y dos líneas más: Why y How to apply.

Un ejemplo del 7 de septiembre. Cuando autorizo una acción arriesgada, suelo añadir una condición de verificación; ese día fue "comprueba en el plan que solo crea el role y la policy". El plan de Terraform mostraba dos creaciones y dos modificaciones, y una de las modificaciones extra revertiría una variable de producción a un valor de ejemplo, lo que habría tumbado el acceso de administrador del sitio. La sesión se detuvo e informó. La memoria que quedó registra la regla, el caso y qué hacer cuando la condición falla: no completar la acción, ni siquiera "solo la parte segura" sin avisar. Sin el why, la próxima sesión trataría la condición como una casilla que marcar.

Los procedimientos van a skills. La de vertex-web, .claude/skills/verify/SKILL.md, existe desde el 10 de julio: cómo levantar el sitio con vertex-api y Postgres, las trampas de la carga de datos y una que me costó tiempo, la de que buscar el texto de un mensaje en la respuesta da falsos positivos, porque cada página incrusta el catálogo completo de next-intl en el payload RSC. Es una receta, no un dato para cada sesión, y la documentación traza la misma línea: si una 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". Hasta que se usa, una skill ocupa en el contexto solo su nombre y su descripción, lo que Anthropic llama progressive disclosure.

Las herramientas también son contexto. Dos veces, un conector MCP añadido a la cuenta en mitad de una sesión no apareció en ella, solo en la siguiente: el de Resend en agosto y el de AWS el 31 de agosto. La documentación no trata ese caso de forma explícita, así que lo tomo como comportamiento observado, no como regla del producto. Aun así, se convirtió en una línea del CLAUDE.md, y las sesiones empiezan comprobando qué herramientas se cargaron.

Y está lo que nunca debe entrar, porque todo lo que pasa por el contexto queda en la transcripción. Las connection strings y las API keys nunca se pegan en el chat ni se imprimen en la salida de una herramienta, y crear una API key a través del conector está prohibido: la respuesta de la herramienta traería el secreto consigo.

Cómo lo verifico hoy

Nada de esto requiere herramientas nuevas, solo verificar en lugar de suponer:

  • Al empezar la sesión, /context: ¿está cada archivo que espero en Memory files? Si el proyecto depende de AGENTS.md, revisar también Project instructions en /config.
  • Instrucción ignorada: ¿se cargó el archivo? ¿Otra instrucción la contradice? ¿Es lo bastante específica como para verificarse?
  • Punteros muertos en cada repositorio público, antes de abrir un PR:
# Archivos versionados que citan documentos que no están en GitHub
git grep -nE "see docs/|CLAUDE\.md|AGENTS\.md" -- . ':!*.md' ':!.gitignore'
  • Cada afirmación de una nota con fecha y método: "verificado el", con el comando o el header que lo probó.
  • Lo que tiene que cumplirse siempre va a un hook o al CI, no al CLAUDE.md.

Por qué importa

El modelo solo sigue lo que ve, y lo ve como un mensaje de usuario, sin garantía de obediencia. Por eso la ingeniería de contexto, en el día a día, trata menos de la prosa del prompt y más de operación: qué se carga, en qué orden, quién gana en un conflicto, qué queda fuera hasta que hace falta, qué no debe entrar nunca y cuán viejo es cada dato.

Ninguno de los fallos que conté tuvo que ver con la capacidad del modelo: un archivo que no se cargó, un documento que nadie consultó, un archivo que desapareció del disco, una frase que envejeció y pendientes que ya estaban resueltos. Todos eran de contexto, y todos los habría detectado alguien que verificara en lugar de suponer. Ese alguien puede ser el propio agente, siempre que el contexto le diga que verifique.

Referencias

Comentarios

Cargando comentarios...