Code

La arquitectura de memoria de Claude Code

Una crónica desde las entrañas del código filtrado de Anthropic — donde los archivos markdown mueren sin hacer ruido, la memoria se reescribe sola, y tu IA finge que nunca te conoció.


La filtración

Alguien publicó el código fuente completo de Claude Code en internet. Así, sin más. Como quien deja una pistola cargada en la mesa del desayuno y se va a preparar café.

Los ingenieros se lanzaron sobre lo predecible:

  • Los prompts del sistema.
  • Las definiciones de herramientas.
  • La lógica de facturación.
  • Las cosas que brillan cuando las abres.

Yo fui directo a src/memdir/.

Siete archivos. Toda la arquitectura de memoria. Y enterrado ahí dentro, como una mina anti-personal en un campo de margaritas, un límite duro que Anthropic jamás documentó públicamente.

Siete benditos archivos:

ArchivoFunción
memdir.tsOrquestador principal del directorio de memoria
findRelevantMemories.tsEl que decide qué recuerdos importan (spoiler: usa Sonnet, no embeddings)
memoryAge.tsCalcula la antigüedad y genera advertencias de frescura
memoryScan.tsEscanea y recopila archivos de memoria del disco
memoryTypes.tsDefine los 4 tipos permitidos — y solo 4
paths.tsRutas al filesystem donde vive todo esto
teamMemPaths.ts / teamMemPrompts.tsMemoria compartida de equipo (detrás de feature flag)

Lo que encontré ahí dentro me hizo tirar el café.


La verdad que nadie te cuenta: está insanamente bien diseñado

Antes de despedazar los límites, hay que reconocer algo. El sistema de memoria de Claude Code no es un hack de fin de semana. Es una arquitectura restringida, estructurada y auto-reparante. No es «guardar todo y rezar». Es ingeniería con opinión.

El código está haciendo cosas profundamente no obvias.

Memoria = índice, no almacenamiento

MEMORY.md siempre se carga en contexto, pero no es un depósito de conocimiento. Es un catálogo de punteros. Cada línea ocupa ~150 caracteres: un nombre de archivo y una descripción de una línea. El conocimiento real vive afuera, en archivos temáticos separados, y solo se trae cuando alguien lo pide.

Es como la diferencia entre un libro y el índice de una biblioteca. MEMORY.md es el catálogo de fichas. Los libros están en los estantes.

Diseño de 3 capas (consciente de ancho de banda)

El sistema tiene tres capas de almacenamiento, cada una con reglas de lectura distintas:

CapaQué contieneCuándo se leeCómo se lee
Capa 1: MEMORY.mdÍndice de punterosSiempre — inyectado en el system promptLectura completa automática
Capa 2: Topic files (*.md)Conocimiento detallado por temaBajo demanda — solo si es relevanteFileReadTool selectivo
Capa 3: Session transcripts (.jsonl)Transcripciones de sesiones pasadasNunca se leen completasSolo grep dirigido con términos específicos

Tres niveles de compromiso:

  • La Capa 1 cuesta contexto en cada turno.
  • La Capa 2 solo cuando la necesitas.
  • La Capa 3 jamás entra completa en contexto — solo se le hacen búsquedas puntuales.

Disciplina estricta de escritura

La regla de escritura es binaria:

  1. Escribir el contenido al archivo temático
  2. Después actualizar el índice con un puntero

Jamás se vuelca contenido directamente en el índice. Esto previene la entropía — la acumulación de basura que contamina el contexto. Si metieras conocimiento detallado dentro de MEMORY.md, cada sesión cargaría todo ese peso muerto. El sistema lo prohíbe por diseño.

Lo que NO almacenan es la verdadera genialidad

Esta es la decisión de diseño más reveladora:

  • No se guardan logs de debugging
  • No se guarda la estructura del código
  • No se guarda el historial de PRs
  • No se guarda nada que sea derivable del codebase actual

Si puedes obtenerlo con grep o git log, no se persiste como memoria. El sistema asume que el código fuente es la fuente de verdad, no los recuerdos sobre el código fuente. Los hechos derivados del código nunca se almacenan.

Esto es filosóficamente potente: la memoria existe solo para lo que no puedes descubrir leyendo el repo.


Cómo funciona realmente el sistema de memoria

Vamos a desnudar esta belleza.

Claude Code almacena recuerdos como archivos markdown planos en tu disco. Así. Archivos .md, en una carpeta. Como si estuviéramos en 2014 y el futuro de la inteligencia artificial dependiera de un README bien formateado.

La ruta: ~/.claude/projects/<nombre-de-tu-repo>/memory/

Cada proyecto tiene su propia carpeta:

  • Cada conversación puede escribir ahí.
  • Los archivos persisten entre sesiones.
  • Ese es todo el modelo de persistencia.
  • Markdown y buena voluntad.

Hay también un archivo índice llamado MEMORY.md. Vive en la raíz de esa carpeta. Claude lo lee al inicio de cada sesión para entender qué recuerdos existen.

Aquí es donde la historia se pone interesante. Aquí es donde empiezas a sentir ese escozor en la nuca que te dice que algo no cuadra.


Las rutas de escritura y lectura

El sistema tiene tres escritores y tres lectores operando sobre las mismas tres capas. Cada uno con reglas distintas.

Escritores (Write paths)

EscritorCuándo operaQué escribe
Manual writeCuando el usuario lo pide explícitamenteEscritura inmediata → archivo + índice
autoDreamEn segundo plano, consolidación periódicaReescritura de archivos temáticos existentes
extractMemoriesPer-turn, captura automáticaNuevas entradas extraídas de la conversación

Lectores (Read paths)

CapaMecanismo de lectura
MEMORY.md (Capa 1)System prompt — siempre inyectado, siempre visible
Topic files (Capa 2)FileReadTool — bajo demanda, solo si findRelevantMemories() los selecciona
Session transcripts (Capa 3)Targeted grep — nunca lectura completa, solo búsqueda por términos estrechos

Las escrituras manuales van en línea sólida — inmediatas, síncronas. Las escrituras de autoDream y extractMemories van en línea punteada — asíncronas, en segundo plano.

Una consolidationLock.ts protege contra condiciones de carrera entre los escritores concurrentes.


El tope de 200 líneas

MEMORY.md tiene dos límites duros grabados a fuego en el código fuente.

200 líneas máximo. Si tu índice crece más allá de eso, el sistema lo trunca en silencio. Silencio total. Silencio de tumba. Le pega una advertencia al contenido truncado, sí, pero esa advertencia solo es visible si vas tú personalmente a abrir el archivo como un detective privado buscando pistas. Claude ve un system prompt limpio y no tiene ni la menor idea de que el índice fue cortado.

25 KB máximo. Un tope secundario en bytes para casos extremos donde cada línea es anormalmente larga.

MAX_ENTRYPOINT_LINES = 200
MAX_ENTRYPOINT_BYTES = 25000

El modo de fallo es silencioso. Llegas a la línea 201. Los recuerdos caen del fondo del índice como pasajeros de un barco que nadie vio hundirse. Claude deja de saber que existen. No te avisa. No lanza un error. No se disculpa. Simplemente olvida.

Y tú sigues hablando con él como si nada, sin saber que tu agente acaba de sufrir una lobotomía selectiva.

Pero la truncación forzada del índice no es un bug. Es la contrapartida del diseño: la obsolescencia es ciudadana de primera clase. Si la memoria no refleja la realidad, la memoria está equivocada. El sistema prefiere olvidar a recordar mal.

La confusión de los dos «200» y los ~150 caracteres

Hay un número que aparece dos veces en la documentación oficial y genera confusión: 200. Pero se refiere a dos sistemas completamente distintos con comportamientos opuestos al exceder el límite.

ArchivoLímiteTipo de límiteQué pasa al excederlo
MEMORY.md (auto memory)200 líneas o 25KBCorte duro — el contenido más allá no se cargaTruncamiento silencioso. Claude no sabe que falta información
CLAUDE.md (instrucciones)200 líneas recomendadasGuía blanda — el archivo se carga completoLa adherencia a las instrucciones degrada progresivamente

La documentación oficial de Anthropic lo confirma:

Auto memory (MEMORY.md): «The first 200 lines of MEMORY.md, or the first 25KB, whichever comes first, are loaded at the start of every conversation. Content beyond that threshold is not loaded at session start.»

CLAUDE.md: «Size: target under 200 lines per CLAUDE.md file. Longer files consume more context and reduce adherence.»

Son sistemas paralelos con el mismo número mágico, pero consecuencias radicalmente diferentes. Uno te amputa recuerdos. El otro te ignora gradualmente.

Y luego están los ~150 caracteres por línea. Este número viene del diseño del índice: cada línea de MEMORY.md es un puntero — un nombre de archivo más una descripción de una línea. No es contenido detallado. Es una ficha de biblioteca. Por eso el promedio ronda los 150 caracteres: es lo que cabe en [nombre-del-archivo.md] — descripción concisa del tema.

Esto significa que el presupuesto real de MEMORY.md es aproximadamente:

200 líneas × ~150 chars/línea = ~30,000 caracteres de índice
(limitado a 25KB, lo que sea menor)

Treinta mil caracteres de punteros, no de conocimiento. El conocimiento está en los archivos temáticos de la Capa 2. El índice solo dice «este archivo existe y habla de esto».


Los cuatro tipos de memoria (y solo cuatro)

El código fuente es explícito. Dictatorial, incluso. Cuatro tipos. Ni más, ni menos. Si no cabe en una de estas categorías, no existe:

TipoQué almacenaAlcance
UserQuién eres. Tu rol, experiencia, preferencias, cómo te gusta comunicarteSolo tú
FeedbackCorrecciones que has dado. Enfoques validados. Cosas que dejar de hacerSolo tú
ProjectLo que pasa en el codebase. Deadlines, decisiones arquitectónicas, contexto que no se puede derivar del códigoProyecto
ReferencePunteros a sistemas externos. Dónde se trackean bugs. Qué canal de Slack vigilarProyecto

La regla de oro en el código: si la información es derivable del codebase actual mediante grep o git, NO debe guardarse como memoria.

Cuatro cajones. Cuatro. Para toda la complejidad de tu relación con una inteligencia artificial que pretende recordarte. Es como intentar organizar tu vida entera en cuatro carpetas de Manila.


La side-call a Sonnet

Cada turno — cada maldito turno — Claude Code hace una llamada API separada a Claude Sonnet. No al modelo principal. A Sonnet. Solo para decidir qué archivos de memoria son relevantes para tu consulta actual.

El proceso:

  1. Escanea todos los archivos de memoria
  2. Extrae sus nombres y descripciones de una línea
  3. Manda ese manifiesto a Sonnet
  4. Le pide que elija los más relevantes
  5. Máximo 5 archivos devueltos
findRelevantMemories() → max 5

Cinco. Puedes tener cien archivos de memoria con el conocimiento acumulado de meses de trabajo, y Sonnet va a leer una lista de nombres de archivos — como un bibliotecario leyendo los lomos de los libros sin abrirlos — y va elegir cinco.

No hay embeddings. No hay búsqueda vectorial. No hay similitud semántica sofisticada. Solo un modelo de lenguaje leyendo una lista y haciendo un juicio de valor. Es como pedirle a alguien que elija los cinco libros más relevantes de una biblioteca basándose exclusivamente en las portadas.

Y aquí hay un matiz crucial del diseño: la recuperación es escéptica, no ciega. La memoria es una pista, no una verdad. El modelo debe verificar antes de usar cualquier recuerdo. No se asume que lo almacenado sigue siendo correcto.


autoDream: la reescritura nocturna de la memoria

Este es el componente más fascinante de la arquitectura. autoDream es un proceso en segundo plano que reescribe activamente la memoria. No solo añade — edita, fusiona, deduplica, elimina contradicciones y poda agresivamente.

Cuándo se dispara

autoDream se activa cuando se cumplen dos condiciones simultáneamente:

  • Han pasado ≥24 horas desde la última consolidación
  • Se han acumulado ≥5 sesiones desde la última ejecución

Se ejecuta mediante un subagente forkeado — aislado del contexto principal. Con herramientas limitadas. No puede corromper la sesión activa.

Las 4 fases de autoDream

FaseNombreQué hace
Phase 1OrientEvalúa el estado actual de la memoria — qué hay, qué falta, qué sobra
Phase 2Gather signalRecolecta señales de sesiones recientes — patrones, correcciones, nuevos hechos
Phase 3ConsolidateFusiona, deduplica, resuelve contradicciones. Convierte memorias vagas en absolutas
Phase 4Prune & indexPoda agresivamente lo obsoleto. Reconstruye el índice limpio

Esto significa que la memoria es continuamente editada, no simplemente anexada. Un append-only log se convertiría en ruido inmanejable. autoDream convierte el sistema en algo auto-reparante.

Ejemplo concreto: si en enero dijiste «usamos Express» y en marzo migraron a Fastify, autoDream eventualmente detecta la contradicción y actualiza la memoria. No mantiene ambas versiones apiladas como capas geológicas.

Aislamiento del subagente

La consolidación opera en un proceso forkeado con herramientas restringidas. El subagente puede leer y escribir archivos de memoria, pero no puede:

  • Ejecutar código
  • Modificar el codebase
  • Interactuar con herramientas de la sesión principal

Esto es aislamiento deliberado. Si el consolidador corrompe algo, el daño se contiene dentro de los archivos de memoria. La sesión activa no se toca.


La frescura de los recuerdos

El código incluye una función memoryFreshnessText() que genera una advertencia de obsolescencia para memorias de más de un día de antigüedad.

La advertencia dice, parafraseado del inglés con todo el sabor clínico del original:

«Esta memoria tiene X días de antigüedad. Las memorias son observaciones puntuales, no estado en vivo. Las afirmaciones sobre comportamiento del código o citaciones [archivo:línea] pueden estar desactualizadas.»

Se inyecta directamente en el contenido de la memoria antes de que Claude lo vea. Claude sabe que los recuerdos viejos podrían estar equivocados. Pero no hay forma, desde fuera, de saber qué memorias activaron esa advertencia.

Y aquí está la trampa mortal: las advertencias de frescura solo se disparan para memorias que fueron cargadas. Si una memoria fue truncada fuera del índice, nunca se carga. No hay advertencia. No hay señal. Claude no sabe lo que no sabe.


El agente fantasma

Existe un agente extractor de memorias — extractMemories — que captura información per-turn durante la conversación. Un proceso de segundo plano revisa lo que pasó y extrae memorias automáticamente.

Esto significa que hay tres entidades distintas escribiendo en tu directorio de memoria:

EscritorCuándoTipo de escritura
Manual writeCuando el usuario lo pideInmediata, síncrona
extractMemoriesCada turno de conversaciónPer-turn capture, segundo plano
autoDreamCada ≥24h + ≥5 sesionesConsolidación masiva, subagente forkeado

El código incluye feature flags para el extractor: EXTRACT_MEMORIES, tengu_passport_quail. No está activado para todos. Pero la arquitectura está ahí, respirando, esperando.

Y — detalle crucial — el código tiene un candado: los escritores son MUTUAMENTE EXCLUYENTES. No pueden escribir al mismo tiempo. Un hasMemoryWriteLock controla la exclusión mutua, respaldado por consolidationLock.ts para protección contra condiciones de carrera. Civilizado, al menos.


Memoria de equipo

Hay un feature flag TEAMMEM para memorias de alcance de equipo.

Cuando está habilitado, algunas memorias son privadas (solo tú las ves) y otras son de equipo (todos los contribuidores las comparten). Las convenciones del proyecto van a la memoria del equipo. Las preferencias personales se quedan privadas.

Es una buena idea. Lástima que esté detrás de un feature flag que la mayoría de usuarios nunca verá activado.


Cómo Claude Code realmente olvida

Aquí está el escenario que rompe todo. La pesadilla silenciosa. El bug que no es un bug sino una feature que nadie te contó.

Has usado Claude Code durante tres meses en un proyecto real. Ha aprendido:

  • Tus preferencias y tu forma de trabajar
  • Las decisiones arquitectónicas tomadas en enero
  • Que un endpoint específico es inestable y no debe usarse en tests
  • Que tu equipo se salta las code reviews para hotfixes
  • Los patrones de código que odias
  • La forma correcta de nombrar variables en tu proyecto

Luego llegas a la entrada 201.

El índice se trunca en silencio. Los recuerdos más antiguos caen del fondo. Claude carga un contexto fresco en la siguiente sesión sin ninguna idea de que esos recuerdos existieron alguna vez.

Lo que pasa después:

  1. Claude escribe un test que apunta al endpoint inestable ✗
  2. Claude te pregunta de nuevo por tu política de code reviews ✗
  3. Claude contradice la arquitectura que acordaron hace meses ✗
  4. Claude usa el patrón de nombrado que le dijiste que odiabas ✗

No está alucinando. No está roto. Simplemente olvidó. Y no tiene forma de decírtelo.

1 a 50 memorias     → Funciona de maravilla
100 a 200 memorias  → Acercándose al cap
201+ memorias       → Truncamiento silencioso
Claude responde     → Falta contexto antiguo
Sin error lanzado   → Nunca te enteras

La truncación le pega una advertencia a MEMORY.md — pero es solo visible si abres el archivo tú mismo. Claude ve un contexto limpio y nunca sabe que le faltan recuerdos.


El sistema de instrucciones CLAUDE.md — la otra capa de memoria

Hay un segundo sistema de memoria que opera en paralelo al directorio memdir/. No es «memoria» en el sentido técnico del código fuente — es el sistema de instrucciones persistentes basado en archivos CLAUDE.md.

Si memdir/ es la memoria autobiográfica de Claude (lo que aprendió de ti), CLAUDE.md es su manual de instrucciones (lo que le dices que haga).

Ambos sistemas convergen en el mismo lugar: el system prompt. Pero sus rutas de descubrimiento, prioridades y reglas de escritura son completamente diferentes.

La jerarquía de 4 niveles

Los archivos CLAUDE.md se cargan en un orden específico. Los que se cargan después tienen mayor prioridad — porque el modelo presta más atención a las instrucciones que aparecen más tarde en la ventana de contexto.

NivelPrioridadRutaAlcancePersistencia
1. ManagedMás baja/etc/claude-code/CLAUDE.mdToda la máquinaControlada por admin/organización
2. UserBaja~/.claude/CLAUDE.md y ~/.claude/rules/*.mdTodos tus proyectosPrivada, nunca en git
3. ProjectAltaCLAUDE.md, .claude/CLAUDE.md, .claude/rules/*.mdEquipo completoCommitted a VCS
4. LocalMás altaCLAUDE.local.md (en cada directorio ancestro)Solo tú, solo este proyectoEn .gitignore

La regla de proximidad: un CLAUDE.md en tu directorio de trabajo actual pesa más que uno en un directorio padre. Los archivos más cercanos al CWD se cargan después → mayor prioridad.

El algoritmo de descubrimiento

Cuando Claude Code arranca, getMemoryFiles() en utils/claudemd.ts camina por el filesystem desde el directorio actual hasta la raíz, recolectando archivos de instrucciones en cada nivel:

1. Archivos managed    → se cargan primero (prioridad más baja)
2. Archivos user       → se cargan segundo
3. Archivos project    → desde la raíz hacia CWD
4. Archivos local      → desde la raíz hacia CWD (prioridad más alta)

La lista completa se memoiza durante la conversación. Si cambias un CLAUDE.md fuera de Claude Code, los cambios no se reflejan hasta que uses /memory (que fuerza un reload) o reinicies la sesión.

Cuándo usar cada nivel

NivelIdeal para
ManagedPolíticas corporativas, restricciones de seguridad, estándares organizacionales
User (~/.claude/CLAUDE.md)Estilo de código preferido, idioma por defecto, tu nombre de usuario en git, atajos personales
Project (CLAUDE.md en el repo)Convenciones del proyecto, notas de arquitectura, comandos de testing, lo que todo contribuidor debe seguir
Local (CLAUDE.local.md)Rutas de tu entorno local, atajos personales que solo aplican a este proyecto, overrides privados

El prefijo que lo cambia todo

Cuando los archivos de memoria se ensamblan, se les antecede con este texto:

«Codebase and user instructions are shown below. Be sure to adhere to these instructions. IMPORTANT: These instructions OVERRIDE any default behavior and you MUST follow them exactly as written.»

Las instrucciones en CLAUDE.md sobreescriben los defaults de Claude. Si Claude normalmente prefiere hacer X, pero tu CLAUDE.md dice «nunca hagas X», gana tu archivo. Esto es el mecanismo para imponer convenciones de proyecto, restringir operaciones y inyectar conocimiento de dominio.


La directiva @include

Los archivos de memoria pueden referenciar otros archivos usando la notación @. El archivo referenciado se inserta en el contexto como una entrada separada antes del archivo que lo incluye.

# Mi proyecto CLAUDE.md

@./docs/architecture.md
@./docs/conventions/typescript.md

Siempre ejecutar `bun test` antes de hacer commit.

Sintaxis soportada

NotaciónResuelve a
@filenameRuta relativa desde el directorio del archivo que incluye
@./relative/pathRuta relativa explícita
@~/home/pathRuta relativa al home del usuario
@/absolute/pathRuta absoluta

Reglas de @include

  • Rutas dentro de bloques de código o inline code se ignoran — solo se procesan nodos de texto plano
  • Referencias circulares se detectan y se saltan
  • Archivos inexistentes se ignoran silenciosamente
  • Profundidad máxima de inclusión: 5 niveles
  • Solo archivos de texto (.md, .ts, .py, .json, etc.) — binarios como imágenes y PDFs se saltan
  • Rutas que apuntan fuera del directorio del proyecto requieren aprobación explícita la primera vez

Archivos de reglas granulares: .claude/rules/*.md

En vez de meter todo en un solo CLAUDE.md monolítico, puedes dividir las instrucciones en múltiples archivos dentro de .claude/rules/:

my-project/
├── CLAUDE.md
└── .claude/
    └── rules/
        ├── testing.md
        ├── typescript-style.md
        └── git-workflow.md

Todos los archivos .md en .claude/rules/ (y subdirectorios) se cargan automáticamente para los niveles project y user.

Reglas con alcance por ruta (path-scoped)

Los archivos de reglas soportan frontmatter para limitar su alcance a rutas específicas:

---
paths:
  - "src/api/**"
  - "src/services/**"
---

Siempre usar inyección de dependencias. Nunca importar implementaciones concretas directamente.

Cuando paths está definido, el archivo de reglas solo se inyecta en contexto cuando Claude está trabajando con un archivo que coincide con uno de los patrones glob. Esto mantiene el contexto ligero en proyectos grandes — no cargas reglas de testing cuando estás editando un componente de UI.


Tamaño máximo por archivo

El máximo recomendado para cualquier archivo de memoria individual es 40,000 caracteres (MAX_MEMORY_CHARACTER_COUNT). Los archivos que exceden este límite se marcan con advertencia, y Claude podría no leer el contenido completo.

MAX_MEMORY_CHARACTER_COUNT = 40000

Mantener los archivos de memoria enfocados y concisos no es un consejo — es un requisito del sistema.


El ensamblaje de contexto: la foto completa

Cada llamada a la API ensambla tres pipelines paralelos que se fusionan en el mensaje:

Pipeline 1: Contexto del sistema

getSystemContext()
├── git status
├── branch / log
└── git user

Pipeline 2: Contexto del usuario

getUserContext()
├── CLAUDE.md (jerarquía de 4 niveles)
├── .claude/rules/*.md (path-scoped)
├── @includes (resueltos recursivamente, max 5 niveles)
├── Inyección de memoria (≤5 archivos de memdir/)
└── Fecha actual

Pipeline 3: Historia

history system
├── pendingEntries[] (buffer en memoria)
├── paste store (≤1024 chars inline, >1024 hash → disco)
└── history.jsonl (~/.claude/, lock + append)

Todo esto converge en el messages[] builder: system + user ctx + history + prompt. Que va directo a la API de Claude. Y cuando el contexto se desborda, entra la compactación — un resumen que comprime la historia y borra aún más.


La compactación: cuando el contexto explota

Cuando la ventana de contexto se llena, Claude Code ejecuta Memory Compact:

  • Resume la conversación hasta el momento
  • classifySummarizedMessages() — hasta 16 mensajes por lote
  • Si el resumen no encaja en la ventana pre-compactación → context reset token
  • Crea nuevos archivos de memoria de sesión (auto-cross-session memories)
  • Produce un resumen de plan, archivo y estado
  • Usa un compact prompt del sistema para guiar la consolidación

La compactación es otro momento donde recuerdos pueden desaparecer. No se pierden del disco — los archivos .md siguen ahí — pero si no están en el índice de 200 líneas, es como si no existieran.


La capa de persistencia: qué vive dónde

~/.claude/
├── CLAUDE.md                        → Instrucciones globales del usuario (nivel 2)
├── rules/*.md                       → Reglas personales globales (nivel 2)
├── projects/
│   └── <project-git-root>/
│       ├── CLAUDE.md                → Instrucciones del proyecto (nivel 3, VCS)
│       └── memory/
│           ├── MEMORY.md            → El índice fatal (200 líneas max)
│           ├── user_role.md         → Archivo temático
│           ├── feedback.md          → Archivo temático
│           └── preferences.md       → Archivo temático
├── memory/
│   └── MEMORY.md                    → Memoria global del usuario
├── agent-memory/MEMORY/
│   └── YYYY-MM-DD.md               → Logs de memoria del agente
└── session-memory/config/           → Plantillas de memoria customizadas

<project-root>/
├── CLAUDE.md                        → Instrucciones del proyecto (nivel 3, VCS)
├── CLAUDE.local.md                  → Instrucciones locales privadas (nivel 4, gitignored)
└── .claude/
    ├── CLAUDE.md                    → Instrucciones adicionales del proyecto (nivel 3)
    └── rules/
        ├── testing.md               → Reglas path-scoped
        ├── typescript-style.md      → Reglas path-scoped
        └── git-workflow.md          → Reglas path-scoped

/etc/claude-code/
└── CLAUDE.md                        → Instrucciones managed/enterprise (nivel 1)

Cada archivo es legible. Editable. Borrable. El modelo puede leer y escribir directamente estos archivos con herramientas estándar: FileReadTool, FileEditTool, FileWriteTool.

La caché readFileCache trackea lo que fue leído. Los permisos de escritura son los mismos del usuario (permission canWrite se chequea antes de escribir).


El loop de retroalimentación cross-sesión

El sistema de memoria forma un loop auto-mejorante a través de sesiones:

Sesión N                          Sesión N+1
┌───────────────────┐             ┌───────────────────┐
│ interacciones →   │             │ interacciones →   │
│ disco: escritura  │ ──────────→ │ disco: lectura    │
│ consolidación     │             │ conversión        │
│ getMemoryFiles()  │             │ findRelevant()    │
└───────────────────┘             └───────────────────┘
         ↓                                 ↓
    archivos .md                  ≤5 archivos cargados
   en el disco                    en el contexto

Timescales:

  • Within-turn: entradas directas + extractMemories per-turn
  • End-of-turn: memorias de sesión consolidadas
  • Cross-session: auto-memorias consolidadas (auto-cross-session)
  • Multi-session: autoDream cada ≥24h + ≥5 sesiones — fusión, poda y reescritura masiva

Fuentes de memoria (en orden de prioridad para el descubrimiento):

  1. MANAGED/etc/claude-code/CLAUDE.md (instrucciones de sistema, prioridad más baja)
  2. USER~/.claude/CLAUDE.md y ~/.claude/rules/*.md (personal, global)
  3. PROJECT_VCS<project>/.claude/CLAUDE.md y .claude/rules/*.md (raíz del git, en VCS)
  4. LOCALCLAUDE.local.md en CWD y ancestros (mayor prioridad, .gitignore)
  5. AUTOGEN MEMORYmemdir/ con MEMORY.md como entrypoint (200 líneas max)
  6. TEMPER — flags de feature para extracción de memorias del equipo

Seguridad y alcance: las fronteras de confianza

AlcanceAlmacenamientoVisibilidadQuién escribe
Managed (enterprise)/etc/claude-code/Global, solo lecturaAdmin/org
User (global)~/.claude/Todas tus sesiones
Project (VCS)<repo>/.claude/Compartido via gitEquipo
Local (privado)CLAUDE.local.md (gitignored)Solo tú, solo este proyecto
Memory (auto)~/.claude/projects/<repo>/memory/Solo tú, solo este proyectoClaude + autoDream

Knobs de configuración que importan:

Variable / FlagEfecto
CLAUDE_CODE_DISABLE_CLAUDE_MDS=1Deshabilita toda la carga de archivos de memoria
--bareSalta el auto-discovery del CWD; solo carga de --add-dir explícitos
claudeMdExcludesPatrones glob de archivos de memoria a excluir
claudeMdPatternsGlobs para encontrar archivos de instrucciones
CLAUDE_CODE_MEMORY_PATH_OVERRIDERuta custom de memoria
CLAUDE_CODE_MEMORY_VCSOverride VCS para memoria
autoUpdatePoliciesSettings (lax, none, raw, etc.)

Los agentes en segundo plano operan con permisos restringidos de herramientas: solo lectura por defecto, escritura solo cuando es explícita.


El comando /memory

Ejecutar /memory dentro del REPL de Claude Code abre el editor de memoria. Muestra qué archivos de memoria están cargados, permite editarlos directamente, y recarga el contexto al guardar.

También puedes pedirle a Claude: «Agrega una regla a CLAUDE.md que siempre usemos indentación de 2 espacios.» Claude localiza el archivo apropiado y escribe la instrucción.


Ejemplo de CLAUDE.md bien estructurado

# Mi Proyecto

## Comandos

- Build: `bun run build`
- Test: `bun test`
- Lint: `bun run lint`
- Type check: `bun run typecheck`

## Arquitectura

Monorepo. Lógica core en `packages/core`. Entrypoint CLI en `src/index.ts`.

## Convenciones

- Usar `zod/v4` para toda validación de esquemas.
- Nunca usar `any` — preferir `unknown` con type guard.
- Todas las funciones async deben manejar errores explícitamente.
- Tests en `__tests__/` junto al archivo fuente.

## Git

- Ramas: `feat/<ticket>-short-description`
- Siempre correr `bun test && bun run typecheck` antes del commit.
- No incluir `console.log` en código commiteado.

@./docs/api-conventions.md

La limitación + la solución

El sistema por defecto es un v1 insanamente bien diseñado. La separación en 3 capas, la disciplina de escritura (índice como punteros, no como almacenamiento), autoDream reescribiendo y podando activamente, la obsolescencia como ciudadana de primera clase, el aislamiento del consolidador en un subagente forkeado. No es un hack — es ingeniería con criterio.

Pero tiene un techo. Y ese techo es bajo, y oscuro, y cuando te golpeas la cabeza contra él no suena ninguna alarma.

200 líneas en el índice.
5 archivos por turno.
Sin embeddings.
Sin búsqueda vectorial.
Truncamiento silencioso.


La moraleja

El sistema por defecto que Anthropic envía es un primer acto brillantemente diseñado. Memoria como índice, no como almacenamiento. Tres capas conscientes del ancho de banda. Un consolidador nocturno que reescribe y poda. Obsolescencia como principio de diseño. El insight fundamental de no almacenar lo que es derivable.

Es el punto de partida correcto para la mayoría de desarrolladores que recién empiezan con Claude Code.

Pero hay un momento — y lo reconoces cuando llegas — en que empiezas a notar las grietas. Claude te pregunta algo que ya le explicaste. Contradice una decisión que tomaron juntos hace semanas. Usa un patrón que le dijiste que odiabas.

No es un bug. Es una característica no documentada. Un techo que nadie te mostró hasta que te golpeaste la cabeza contra él en la oscuridad.

200 líneas. 5 archivos. Sin embeddings. El truncamiento silencioso.

Ahora lo sabes. Y saber es la primera mitad de la solución.