🧠 Overview & Mental Model
Cada conversa e uma session com UUID, arquivo JSONL de transcript e colecao de metadados. O gerenciamento de sessao abrange 6 camadas:
JSONL append-only sob ~/.claude/projects/. Escritas async via fila por arquivo.
Maquina de estados tri-state: idle / running / requires_action.
Chain-walk da linked list parentUuid, filtros de deserializacao.
Deteccao de turno interrompido -> mensagem sintetica "Continue from where you left off."
Dual paths: v1 Session Ingress (REST) e CCR v2 internal events.
Fetch paginado de eventos para replay remoto de sessao.
📁 Disk Layout & Write Queue
~/.claude/projects/
-Users-alice-myproject/
550e8400-...-.jsonl // main transcript
550e8400-.../
subagents/
agent-<agentId>.jsonl // sidechain transcript
Todas as escritas passam pelo singleton Project que agrupa entradas em filas por arquivo e drena a cada 100ms (ou 10ms quando persistencia cloud esta ativa).
💡 Lazy File Materialization
O arquivo de sessao NAO e criado imediatamente. E deferido ate a primeira mensagem real user/assistant chegar, prevenindo sessoes metadata-only de poluir a lista de resume.
🔗 The parentUuid Linked List
Mensagens formam uma linked list via parentUuid. Ao carregar, Claude Code caminha da folha mais nova ate a raiz, depois reverte:
[user-A] <-- [asst-B] <-- [user-C] <-- [asst-D] <-- [user-E]
uuid=A uuid=B uuid=C uuid=D uuid=E
parent=null parent=A parent=B parent=C parent=D
Load: E -> D -> C -> B -> A -> null
.reverse() -> [A, B, C, D, E]
- Sidechains (subagent):
isSidechain=true, arquivo separado - Forks: novo sessionId, copia source chain, re-stampa sessionId
- Compact boundary:
parentUuid=null(trunca chain no ponto de compaction)
🎭 Session State Machine
stateDiagram-v2
[*] --> idle
idle --> running : user sends message
running --> idle : model finishes / error
running --> requires_action : tool needs approval
requires_action --> running : user approves/denies
Transicoes disparam callbacks de listener que alimentam: o bridge CCR (para notificacoes requires_action), o event stream SDK (VS Code / scmuxd) e o metadata store externo.
🔄 Conversation Recovery
Quando Claude Code carrega um transcript para resume, determina se a sessao anterior foi interrompida mid-turn:
none
Turno completou normalmente. Nenhuma acao necessaria.
interrupted_prompt
Usuario enviou mensagem, AI nunca respondeu. SDK auto-retry.
interrupted_turn
AI estava mid-tool-use. Injeta "Continue from where you left off."
🌍 Cloud Persistence
Para sessoes CCR, transcripts sao espelhados na cloud por dois caminhos distintos:
CCR v2 path
internalEventWriter('transcript', entry)
FLUSH_INTERVAL = 10ms
v1 Session Ingress
sessionIngress.appendSessionLog()
REST POST para remoteIngressUrl
Na reconexao, hydrateFromCCRv2InternalEvents() busca eventos remotos e sobrescreve o JSONL local com o transcript autoritativo do servidor.
📊 Session Listing -- Lite Reads
O resume picker precisa mostrar lista de sessoes sem parsear arquivos JSONL completos (que podem ter varios GB). Solucionado por readSessionLite que le apenas o head (primeiros KB) e tail (ultimos 64 KB):
- HEAD (4KB): primeiro prompt, gitBranch, cwd
- TAIL (64KB): customTitle, tag, lastPrompt
Por isso reAppendSessionMetadata move title/tag/last-prompt para EOF no exit e compaction -- garante que esses campos fiquem dentro da janela de 64KB que o listing le.
🛠️ Deduplication & Guards
O write path usa um messageSet: Set<UUID> por sessao para pular entradas ja gravadas. Entradas de sidechain de agente NAO sao deduplicadas contra o messageSet principal (vao para arquivo separado).
Persistence Guards
NODE_ENV === 'test'(exceto com TEST_ENABLE_SESSION_PERSISTENCE)cleanupPeriodDays === 0--no-session-persistenceflagCLAUDE_CODE_SKIP_PROMPT_HISTORYenv var