📁 Onde vivem os logs
Tudo começa num único lugar. O Claude Code grava cada conversa em
~/.claude/projects/<projeto>/<sessão>.jsonl:
um arquivo por sessão, organizado em uma pasta por projeto. Some isso por meses de uso
e você tem milhares de arquivos — uma mina inteira de comportamento, esperando para ser garimpada.
🗺️ Conceito principal
Não há banco de dados, nem servidor: é só o sistema de arquivos. Isso é uma boa notícia — você pode varrer tudo com ferramentas comuns.
- •Pasta raiz:
~/.claude/projects/ - •Uma subpasta por projeto (caminho saneado).
- •Um arquivo
.jsonlpor sessão.
~/.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 arquivo = 1 sessão
milhares de arquivos
só o filesystem
📜 JSONL = um evento JSON por linha
Cuidado com a armadilha: o arquivo não é um único objeto JSON gigante. JSONL significa JSON Lines — cada linha é um objeto JSON completo e independente, anexado conforme a conversa avança. É um 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",...}
✓ O que FAZER
- ✓Ler linha a linha (
for line in f). - ✓Fazer
json.loadspor linha. - ✓Tolerar linha quebrada (pular, não abortar).
✗ O que NÃO fazer
- ✗
json.load(arquivo_inteiro)— quebra. - ✗Assumir que a última linha está completa.
- ✗Carregar tudo na memória de uma vez.
💡 Dica prática
Como é append-only, uma sessão interrompida ainda é legível até a última linha completa. Sempre processe defensivamente: try/except em volta do json.loads de cada linha.
JSON Lines
append-only
linha a linha
tolera interrupção
🧬 Anatomia de um evento
Cada linha carrega um punhado de campos previsíveis. Os principais: type (user/assistant/system/summary),
message (o conteúdo de fato), timestamp, uuid,
cwd e gitBranch. Conhecer esses campos é o que destrava filtrar, agrupar e medir.
type
Classifica o evento: user (você), assistant (modelo), system (harness/hooks), summary (resumo de contexto).
message
O payload: role, model (só no assistente) e content (a lista de blocos).
timestamp · uuid · cwd · gitBranch
Metadados de contexto: ordem temporal, identidade do evento, diretório de trabalho e branch git. Dão o "onde" e "quando".
🔎 Por que isso importa
O timestamp ordena os turnos; o type separa quem falou; e o cwd/gitBranch ajudam a reconstruir o que estava acontecendo. Sem esses campos, o log seria texto sem estrutura.
quem falou
o conteúdo
a ordem
o contexto
🧱 Os blocos de conteúdo do assistente
O assistente não fala em texto corrido — fala em blocos:
text (o que você lê), thinking (raciocínio),
tool_use (chamada de ferramenta) e tool_result (a resposta da ferramenta).
E aqui está o detalhe crucial: o Claude Code grava CADA bloco numa LINHA separada.
🧩 Os quatro blocos
- •
text— a fala visível do modelo. - •
thinking— o raciocínio (cifrado nos logs — voltamos a isso no 1.2). - •
tool_use— o modelo decide chamar uma ferramenta. - •
tool_result— a saída da ferramenta volta (gordura, no 1.2).
⚠️ A pegadinha que dilui o sinal
Como cada bloco vira uma linha, contar "por linha" infla artificialmente os números. Um único turno do assistente pode virar 5 linhas (1 thinking + 1 text + 3 tool_use). Se você medir por linha, perde a noção de "quanto o modelo fez em resposta a um prompt". A correção vem no tópico 6: agrupar em turnos lógicos.
fala visível
raciocínio
decisão de agir
= 1 linha
🏅 message.model — o campo de OURO
De todos os campos, um é o coração do curso: message.model. Ele diz exatamente
qual modelo escreveu cada turno — claude-fable-5,
claude-opus-4-8, claude-haiku-4-5... É o que permite filtrar o log por modelo e separar o corpus de cada um.
{"type":"assistant","message":{
"role":"assistant",
"model":"claude-fable-5",
"content":[ {"type":"thinking",...}, {"type":"text",...} ]
}}
📊 O que ele destrava
- Filtrar por modelo: isolar tudo que o Fable-5 fez vs tudo que o Opus-4.8 fez.
- Comparar: medir cada corpus separadamente e tirar o delta.
- Atribuir: cada turno tem dono — não há ambiguidade.
💡 Dica prática
Só eventos de assistant têm model. Eventos de usuário e de sistema não. Ao agrupar em turnos lógicos, o modelo do turno é o do(s) evento(s) de assistente dentro dele.
message.model
só o assistente
filtrar por modelo
corpus por modelo
🔗 Turno físico (linha) vs turno lógico
A última peça antes de medir qualquer coisa: a unidade certa. Um turno físico é uma linha do arquivo. Um turno lógico é tudo entre um prompt humano e o próximo — o pensamento, o texto e todas as ferramentas que o modelo usou para responder àquele prompt.
✓ Medir por turno lógico
- ✓"O modelo pensou antes de agir neste turno?"
- ✓"Quantas ferramentas usou para responder?"
- ✓Reflete o ritmo de trabalho real.
✗ Medir por linha física
- ✗Infla a contagem (5 linhas = 1 resposta).
- ✗Dilui a presença de raciocínio.
- ✗Mistura blocos de turnos diferentes.
1 linha
prompt → prompt
prompt humano
o ritmo
🪙 Resumo do Módulo
~/.claude/projects — um arquivo .jsonl por sessão.message.model é o campo de ouro — separa Fable de Opus.Próximo Módulo:
1.2 — Gordura vs Ouro, e o Mito do Raciocínio Minerável