{"slug": "the-transcript-is-the-record-reading-what-a-coding-agent-actually-did", "title": "The transcript is the record: reading what a coding agent actually did", "summary": "A developer who builds Ordewell, an Apache 2.0 tool for reading coding-agent session files, describes how Claude Code, Codex and OpenCode each persist a structured per-turn record of their runs — Claude Code as JSONL under ~/.claude/projects, Codex as rollout files under ~/.codex/sessions, and OpenCode in a SQLite database — and argues these on-disk transcripts are a more reliable audit trail than terminal scrollback. The developer recommends identifying the right session by filtering on the munged working-directory folder, sorting by modification time, and then confirming identity via a unique completion marker the agent was prompted to print, noting the transcript records the agent's claims rather than proof of correct work.", "body_md": "Every coding agent writes its own session down. Not the cheerful summary it prints when it stops, and not your scrollback, which died the moment you resized the window. A structured file, one record per turn, on your disk. It is the closest thing these tools have to an audit trail, and almost nobody opens it.\n\n**Disclosure:** I build Ordewell, so the store formats below are the ones my tool reads and the paths are the ones it looks in. It is free and Apache 2.0. The paths and the finding method are true of Claude Code, Codex and OpenCode whether or not you ever install what I built.\n\nA terminal UI repaints. A spinner overwrites a line. A collapsed tool call hides the command it ran. The buffer holds what fits and forgets the rest. Anything that judges a task by reading the terminal is reconstructing meaning from a rendering, which is fragile exactly when it matters, at the end of a long run when the interesting part has already scrolled away.\n\nThe agent writes something better while it works. Claude Code appends one JSON record per line to a session file. Codex writes a rollout file per session. OpenCode keeps messages and typed content parts in a SQLite database. Three shapes, same idea: the agent's own account of each turn, written as it goes, not painted afterwards.\n\n```\nclaude-code   ~/.claude/projects/<munged-cwd>/<session-id>.jsonl\ncodex         ~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl\nopencode      ~/.local/share/opencode/opencode.db   (sqlite: session, message, part)\n\nClaude Code also honours CLAUDE_CONFIG_DIR, which is where a\nsubprocess or a containerised session will have written.\n```\n\nParsing the file is easy. Deciding which file is this task's is the whole problem, because a busy repository accumulates a session per run. Three signals, in order of how much they are worth.\n\n**Directory.** Claude Code names its project folder after the working directory, with every non alphanumeric character turned into a hyphen. That is a one to one mapping you can compute yourself, which makes it a good first filter and a bad final answer. Paths longer than 200 characters get shortened and given a hash by Claude Code itself, so a deep worktree path can land in a folder whose name you cannot predict.\n\n**Time.** A session file is created when the agent starts. Anything last modified before your task began is an earlier run in the same directory, so it can be dropped without opening it. This is a filter, not a decision. Two tasks in the same folder minutes apart still collide.\n\n**The completion marker.** This one decides. If your prompt asks the agent to print a unique marker when it is finished, then the transcript that contains that marker is the transcript of this task, and the ones that do not are other people's work. The marker is doing the identity work that a session id cannot, because the ids are minted by the agent and never handed back to you.\n\nRead in that order, the search stays small: filter by directory, sort by modification time, then open candidates newest first until one carries the marker. The first one that does is your task's record.\n\nWhat you extract is the agent's final answer as it was written, joined from the last assistant turn's text blocks, with none of the decoration the interface added. For a plan of tasks where each one signs off with a marker, that is a clean end to the run: the marker is there, and the text next to it is what the agent said about the work.\n\nWhat is not in there is a tidy timeline of the commands that ran, the diff that came out, or any judgement about whether the work is good. A transcript tells you what the agent said about itself. That is a record of claims, not proof, and the two get confused constantly in this space. The diff in your worktree is what settles whether the work is right.\n\nYou do not need anything I wrote to do this. Pick the newest session file for the project, keep the assistant records, pull the text blocks out, and read the tail.\n\n``` bash\n$ f=$(ls -t ~/.claude/projects/*my-project*/*.jsonl | head -1)\n$ jq -r 'select(.type==\"assistant\" and (.isSidechain|not))\n      | .message.content[]? | select(.type==\"text\") | .text' \"$f\" | tail -40\n```\n\nCodex is the same idea with a different envelope: each line carries a timestamp and a payload, the assistant text sits under response items whose role is assistant, and a session metadata record names the working directory the session was started in. Checking that last field is worth the effort, because a session started somewhere else should not answer for the task you are asking about.\n\nOpenCode migrated from files to SQLite partway through 2026. Claude Code munges directory names and shortens the long ones. Codex thread ids are minted by the server, so no local naming scheme can recover them. None of this is a published interface, which is exactly why a reader built on top of it has to be defensive: a missing file, a line that will not parse, or a shape that drifted returns nothing instead of throwing, and the tool falls back to reading the terminal.\n\nTreat the paths as a convenience, not an API contract. If an agent ships an update and your reader goes quiet, the correct outcome is a fallback and a still working run, not an exception at the end of an hour of work.\n\nThe reader described above, including the defensive fallbacks: [transcriptCapture.ts](https://github.com/ordewell/ordewell/blob/main/packages/core/src/services/transcriptCapture.ts), the interface it satisfies [TaskOutputSource.ts](https://github.com/ordewell/ordewell/blob/main/packages/core/src/interfaces/TaskOutputSource.ts), and [github.com/ordewell/ordewell](https://github.com/ordewell/ordewell).", "url": "https://wpnews.pro/news/the-transcript-is-the-record-reading-what-a-coding-agent-actually-did", "canonical_source": "https://dev.to/ordewell/the-transcript-is-the-record-reading-what-a-coding-agent-actually-did-1nf0", "published_at": "2026-10-02 13:01:44+00:00", "updated_at": "2026-10-02 13:08:01.013822+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "mlops"], "entities": ["Ordewell", "Claude Code", "Codex", "OpenCode", "Anthropic", "OpenAI"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/the-transcript-is-the-record-reading-what-a-coding-agent-actually-did", "markdown": "https://wpnews.pro/news/the-transcript-is-the-record-reading-what-a-coding-agent-actually-did.md", "text": "https://wpnews.pro/news/the-transcript-is-the-record-reading-what-a-coding-agent-actually-did.txt", "jsonld": "https://wpnews.pro/news/the-transcript-is-the-record-reading-what-a-coding-agent-actually-did.jsonld"}}