📁 Dónde viven los logs
Todo empieza en un solo lugar. Claude Code registra cada conversación en
~/.claude/projects/<projeto>/<sessão>.jsonl:
un archivo por sesión, organizado en una carpeta por proyecto. Suma eso durante meses de uso
y tienes miles de archivos — una mina entera de comportamiento, esperando a ser excavada.
🗺️ Concepto principal
No hay base de datos ni servidor: solo el sistema de archivos. Esto es una buena noticia: puedes revisar todo con herramientas comunes.
- •Carpeta raíz:
~/.claude/projects/ - •Una subcarpeta por proyecto (ruta saneada).
- •Un archivo
.jsonlpor sesión.
~/.claude/projects/ ├── -home-voce-projetos-fablelite/ │ ├── 2f3a…-9c.jsonl # sessão 1 │ └── 7b1c…-4e.jsonl # sessão 2 └── -home-voce-projetos-portal/ └── a01d…-22.jsonl
~/.claude/projects
1 archivo = 1 sesión
miles de archivos
solo el filesystem
📜 JSONL = un evento JSON por línea
Cuidado con la trampa: el archivo no es un único objeto JSON gigante. JSONL significa JSON Lines — cada línea es un objeto JSON completo e independiente, agregado a medida que avanza la conversación. Es un formato de streaming, append-only.
{"type":"user","message":{...},"timestamp":"2026-06-10T14:00:01Z"}
{"type":"assistant","message":{"model":"claude-fable-5",...}}
{"type":"assistant","message":{"model":"claude-fable-5",...}}
{"type":"system","subtype":"hook",...}
✓ Qué HACER
- ✓Leer línea por línea (
for line in f). - ✓Hacer
json.loadspor línea. - ✓Tolerar líneas partidas (omitir, no abortar).
✗ Qué NO hacer
- ✗
json.load(arquivo_inteiro)— se rompe. - ✗Suponer que la última línea está completa.
- ✗Cargar todo en la memoria de una sola vez.
💡 Consejo práctico
Como es append-only, una sesión interrumpida sigue siendo legible hasta la última línea completa. Procesa siempre de forma defensiva: try/except alrededor de json.loads de cada línea.
JSON Lines
append-only
línea por línea
tolera interrupciones
🧬 Anatomía de un evento
Cada línea incluye un puñado de campos predecibles. Los principales: type (user/assistant/system/summary),
message (el contenido real), timestamp, uuid,
cwd e gitBranch. Conocer estos campos es lo que permite filtrar, agrupar y medir.
type
Clasifica el evento: user (tú), assistant (modelo), system (harness/hooks), summary (resumen de contexto).
message
El payload: role, model (solo en el asistente) y content (la lista de bloques).
timestamp · uuid · cwd · gitBranch
Metadatos de contexto: orden temporal, identidad del evento, directorio de trabajo y rama de git. Dan el «dónde» y el «cuándo».
🔎 Por qué esto importa
O timestamp ordena los turnos; el type separa quién habló; y el cwd/gitBranch ayudan a reconstruir lo que estaba pasando. Sin estos campos, el registro sería texto sin estructura.
quién habló
el contenido
el orden
el contexto
🧱 Los bloques de contenido del asistente
El asistente no habla en texto corrido: habla en bloques:
text (lo que lees), thinking (razonamiento),
tool_use (llamada a herramienta) y tool_result (la respuesta de la herramienta).
Y aquí está el detalle crucial: Claude Code registra CADA bloque en una LÍNEA separada.
🧩 Los cuatro bloques
- •
text— la expresión visible del modelo. - •
thinking— el razonamiento (cifrado en los logs; volvemos a esto en el 1.2). - •
tool_use— el modelo decide llamar a una herramienta. - •
tool_result— vuelve la salida de la herramienta (sobrecarga, en el 1.2).
⚠️ La trampa que diluye la señal
Como cada bloque se convierte en una línea, contar «por línea» infla artificialmente los números. Un solo turno del asistente puede convertirse en 5 líneas (1 thinking + 1 text + 3 tool_use). Si mides por línea, pierdes la noción de «cuánto hizo el modelo en respuesta a un prompt». La corrección está en el tema 6: agrupar en turnos lógicos.
discurso visible
razonamiento
decisión de actuar
= 1 línea
🏅 message.model — el campo de ORO
De todos los campos, uno es el corazón del curso: message.model. Dice exactamente
qué modelo escribió cada turno — claude-fable-5,
claude-opus-4-8, claude-haiku-4-5... Es lo que permite filtrar el log por modelo y separar el corpus de cada uno.
{"type":"assistant","message":{
"role":"assistant",
"model":"claude-fable-5",
"content":[ {"type":"thinking",...}, {"type":"text",...} ]
}}
📊 Lo que permite hacer
- Filtrar por modelo: aislar todo lo que hizo Fable-5 frente a todo lo que hizo Opus-4.8.
- Comparar: medir cada corpus por separado y calcular la diferencia.
- Asignar: cada turno tiene un responsable; no hay ambigüedad.
💡 Consejo práctico
Solo eventos de assistant tienen model. Los eventos de usuario y del sistema, no. Al agrupar en turnos lógicos, el modelo del turno es el de los eventos de asistente que contiene.
message.model
solo el asistente
filtrar por modelo
corpus por modelo
🔗 Turno físico (línea) vs. turno lógico
La última pieza antes de medir cualquier cosa: la unidad correcta. Un turno físico es una línea del archivo. Un turno lógico es todo entre un prompt humano y el siguiente — el pensamiento, el texto y todas las herramientas que el modelo usó para responder a ese prompt.
✓ Medir por turno lógico
- ✓"¿El modelo pensó antes de actuar en este turno?"
- ✓"¿Cuántas herramientas usaste para responder?"
- ✓Refleja el ritmo de trabajo real.
✗ Medir por línea física
- ✗Infla el recuento (5 líneas = 1 respuesta).
- ✗Diluyen la presencia del razonamiento.
- ✗Mezcla bloques de turnos diferentes.
1 línea
prompt → prompt
prompt humano
el ritmo
🪙 Resumen del módulo
~/.claude/projects — un archivo .jsonl por sesión.message.model es el campo de oro — separa Fable de Opus.Próximo módulo:
1.2 — Grasa vs. Oro, y el mito del razonamiento minable