PTENES
Skip to content
MODULE 1.1

📂 Where Conversations Live and the Anatomy of a Session

To mine the gold, you first need to find the mine. In this module: the path on disk, the JSONL format, the anatomy of an event, the content blocks, the field message.model and the right unit of measurement — the logical turn.

6
Topics
25
Minutes
Basic
Level
Theory
Type
~/.claude/projects log root <project>/ one folder per project 2f3a…-9c.jsonl (session 1) 7b1c…-4e.jsonl (session 2) …thousands in total
1

📁 Where the logs live

It all starts in one place. Claude Code records every conversation in ~/.claude/projects/<projeto>/<sessão>.jsonl: one file per session, organized in a folder for each project. Add that up over months of use, and you have thousands of files — an entire gold mine of behavior, waiting to be mined.

🗺️ Main concept

There's no database or server: just the file system. That's good news — you can scan everything with common tools.

  • •Root folder: ~/.claude/projects/
  • •One subfolder per project (sanitized path).
  • •One file .jsonl per session.
~/.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
Path

~/.claude/projects

Granularity

1 file = 1 session

Scale

thousands of files

Access

filesystem only

2

📜 JSONL = one JSON event per line

Watch out for the trap: the file no is one huge JSON object. JSONL means JSON Lines — each line is a complete, independent JSON object, appended as the conversation progresses. It’s a format for 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",...}

✓ What to DO

  • ✓Read line by line (for line in f).
  • ✓Do json.loads per line.
  • ✓Tolerate a broken line (skip it, don’t abort).

✗ What NOT to do

  • ✗json.load(arquivo_inteiro) — breaks.
  • ✗Assume the last line is complete.
  • ✗Load everything into memory at once.

💡 Practical tip

Since it’s append-only, an interrupted session is still readable up to the last complete line. Always process it defensively: try/except around the json.loads of each line.

Format

JSON Lines

Writing

append-only

Reading

line by line

Robustness

tolerates interruption

3

🧬 Anatomy of an event

Each line carries a handful of predictable fields. The main ones: type (user/assistant/system/summary), message (the actual content), timestamp, uuid, cwd e gitBranch. Knowing these fields is what unlocks filtering, grouping, and measuring.

›

type

Classifies the event: user (you), assistant (model), system (harness/hooks), summary (context summary).

›

message

The payload: role, model (assistant-only) and content (the list of blocks).

›

timestamp · uuid · cwd · gitBranch

Context metadata: chronological order, event identity, working directory, and git branch. They show the "where" and "when".

🔎 Why this matters

O timestamp orders the turns; the type separates who spoke; and the cwd/gitBranch help reconstruct what was happening. Without these fields, the log would be unstructured text.

type

who spoke

message

the content

timestamp

the order

cwd/branch

the context

4

🧱 The assistant’s content blocks

The assistant doesn't speak in continuous prose — it speaks in blocks: text (what you read), thinking (reasoning), tool_use (tool call) and tool_result (the tool’s response). And here’s the crucial detail: Claude Code records EACH block on a SEPARATE LINE.

🧩 The four blocks

  • •text — the model’s visible output.
  • •thinking — the reasoning (encrypted in the logs — we'll return to this in 1.2).
  • •tool_use — the model decides to call a tool.
  • •tool_result — the tool output comes back (overhead, in 1.2).

⚠️ The pitfall that dilutes the signal

Because each block becomes a line, counting “per line” artificially inflates the numbers. A single assistant turn can become 5 lines (1 thinking + 1 text + 3 tool_use). If you measure by line, you lose track of “how much the model did in response to a prompt.” The fix comes in topic 6: group into logical turns.

text

visible speech

thinking

reasoning

tool_use

decision to act

1 block

= 1 line

5

🏅 message.model — the GOLD field

Of all the fields, one is at the heart of the course: message.model. It says exactly which model wrote each turn — claude-fable-5, claude-opus-4-8, claude-haiku-4-5... That’s what lets you filter the log by model and separate each model’s corpus.

{"type":"assistant","message":{
    "role":"assistant",
    "model":"claude-fable-5",
    "content":[ {"type":"thinking",...}, {"type":"text",...} ]
}}

📊 What it unlocks

  • Filter by model: isolate everything Fable-5 did from everything Opus-4.8 did.
  • Compare: measure each corpus separately and calculate the delta.
  • Assign: each turn has an owner — there is no ambiguity.

💡 Practical tip

Only assistant have model. User and system events do not count. When grouping into logical turns, the turn’s model is the model of the assistant event(s) within it.

Where

message.model

Who has

assistant only

What for

filter by model

Result

corpus per model

6

🔗 Physical turn (line) vs. logical turn

The last piece before measuring anything: the right unit. One physical turn is one line in the file. A logical turn is everything between one human prompt and the next — the thinking, the text, and all the tools the model used to answer that prompt.

prompt 1 logical turn line: thinking line: text line: tool_use (Read) line: tool_use (Edit) line: tool_use (Bash) next prompt

✓ Measure by logical turn

  • ✓“Did the model think before acting this turn?”
  • ✓“How many tools did you use to answer?”
  • ✓Reflects the pace of real work.

✗ Measure by physical line

  • ✗Inflates the count (5 lines = 1 response).
  • ✗Dilutes the presence of reasoning.
  • ✗Mixes blocks from different turns.
Physical turn

1 line

Logical turn

prompt → prompt

Group by

human prompt

Reveals

the pace

🪙 Module Summary

✓
The logs live in ~/.claude/projects — one .jsonl file per session.
✓
JSONL = one event per line — append-only streaming, read line by line.
✓
Each event has type/message/timestamp/uuid/cwd/gitBranch — the structure for filtering and measuring.
✓
The assistant speaks in blocks, 1 per line — that's why counting "per line" dilutes the signal.
✓
message.model is the gold field — separates Fable from Opus.
✓
The logical turn is the right unit — from prompt to prompt.

Next Module:

1.2 — Fat vs. Gold, and the Myth of Mineable Reasoning