DEV Community

Takashi Matsuyama
Takashi Matsuyama

Posted on • Originally published at blog.tak3.jp

Add a Verifiable, Replayable Trail to Your Claude Code Sessions in 5 Minutes

Claude Code is already keeping a diary in your repos. It just keeps it as a pile of vendor logs you will never read back — not readable, not verifiable, and nowhere near your code.

Five minutes from now, that changes. You won't change anything about your workflow. And your past sessions are included — retroactively.

What you'll have in five minutes

A .basou/ directory next to your code, holding:

  • handoff.md, for humans — where things stand, what happened, where to pick up
  • per-session events.jsonl files under .basou/sessions/, the source of truth — append-only, hash-chained event logs
  • and basou verify, a machine answer to the question "has this record been tampered with?"

The full picture of basou is in the introduction post. In one line: it's a local-first harness — a full set of tack — for AI coding agents: a saddle (a declarative workspace), reins (orientation that carries intent), and, underneath both, a replayable record of what the agent actually did. Today is hands-on with that bottom layer only: the trail.

Prerequisites and Step 1: install and init (30 seconds)

Three prerequisites: Node.js 20.10.0 or later, Claude Code, and a real repository you've been using Claude Code in.

npm install -g @basou/cli
cd path/to/your-repo
basou init
Enter fullscreen mode Exit fullscreen mode

This article is written against basou v0.34.0 (as of 2026-07-14).

One thing to know up front: what ends up in git. By default, basou keeps the noisy parts out of git — the raw event log, internal logs, and the live orientation view — and leaves the durable summaries trackable: the manifest, handoff.md, decisions.md, and each session's session.yaml, so you can review them in a PR. Those summaries carry real work content — session labels, decision titles, the handoff text itself — so if you'd rather commit nothing at all, run basou init --local-only and the whole .basou/ directory stays gitignored.

Step 2: import what's already there — basou import

This is the moment the title promised.

basou import claude-code --all
Enter fullscreen mode Exit fullscreen mode
Imported sessions: 20 path(s) sanitized
Imported 4 session(s) (101 events)
Enter fullscreen mode Exit fullscreen mode

One command, and four Claude Code sessions I had run in this repository — 101 events' worth — became a trail, retroactively. --all just means "everything you can find for this repo"; there is no path to point at and no config to write — basou auto-detects the native logs on disk. If you'd rather look before you leap, add --dry-run first: it writes nothing and returns a preview — Dry run: would import ….

The finer points and the usual gotchas are in the quickstart. The one thing to take away here: you changed nothing about your workflow, and your history became a trail anyway.

Step 3: generate the handoff, read it back — and what "replayable" means

What the import wrote is the record: append-only, hash-chained JSONL under .basou/sessions/. The human view of it is something you ask for:

basou handoff generate
Enter fullscreen mode Exit fullscreen mode
# Handoff

> Generated at 2026-07-13T16:31:18.106Z from ses_01KXE534X7..ses_01KXE534XZ

## Current state

- Last session: claude-code 2026-06-14: 23 commands, 3 files (imported) [ses_01KXE534X7]

## Latest decision

- ████████ の AGENTS.md 文書そのもの(散文本文)の言語をどう改定しますか? … -> 本文は日本語のまま維持 [decision_01KXE534X0]

(3 decisions total — see decisions.md)
Enter fullscreen mode Exit fullscreen mode

(The Japanese line under Latest decision is my data — a decision title — and basou keeps user data verbatim; this is a real workspace of mine, not a staged demo. The view chrome itself defaults to English; declare language: ja on the anchor repo in the manifest and the headings render in Japanese.)

This is an excerpt of .basou/handoff.md — where things stand, what happened last, where to pick up. Point a teammate at it. Point tomorrow's you at it. Point the next agent session at it.

Now, the part that earns the word replayable in the title. The Markdown you just read is not the record — it's a view, derived from the record. Which means it's disposable. Let's prove it. I could rm it, but I'll move it aside so we can diff afterwards:

mv .basou/handoff.md /tmp/before.md
basou handoff generate
diff /tmp/before.md .basou/handoff.md
Enter fullscreen mode Exit fullscreen mode
4c4
< > Generated at 2026-07-13T16:31:18.106Z from ses_01KXE534X7..ses_01KXE534XZ
---
> > Generated at 2026-07-13T16:32:02.596Z from ses_01KXE534X7..ses_01KXE534XZ
Enter fullscreen mode Exit fullscreen mode

One line moved: the generation timestamp. Everything else came back byte-identical — re-derived from events.jsonl, deterministically, offline, with no LLM involved. Same command, same view, resurrected from the source of truth.

That's what replayable means here. There is no step-through player — there is a source log you can re-consume at any time, and every human-readable view can be rebuilt from it. The handoff, the decision log, the orientation — each is a cheap projection of one verifiable log. Hand-edit the views if you like; when you want the log's version back, regenerate it.

Step 4: verify — and what "verifiable" means

Peek into a session's events.jsonl and you'll see each event holding the hash of the one before it:

{"schema_version":"0.1.0","id":"evt_01KXE534X7…","session_id":"ses_01KXE534X7…","occurred_at":"2026-06-14T06:10:20.317Z","source":"claude-code-import","type":"session_started","prev_hash":"12d50f19…2f70014"}
{"schema_version":"0.1.0","id":"evt_01KXE534X7…","session_id":"ses_01KXE534X7…","occurred_at":"2026-06-14T06:10:25.021Z","source":"claude-code-import","type":"command_executed","command":"bash",  ,"prev_hash":"793df9dd…330ac00"}
Enter fullscreen mode Exit fullscreen mode

Which is why a machine can answer the question: "has this record been tampered with since it was written?"

basou verify
Enter fullscreen mode Exit fullscreen mode
ses_01KXE534X7…  verified (34 events)
ses_01KXE534XJ…  verified (19 events)
ses_01KXE534XV…  verified (45 events)
ses_01KXE534XZ…  verified (3 events)
Sessions: 4 total — 4 verified, 0 unchained, 0 empty, 0 incomplete, 0 in_progress, 0 tampered
Enter fullscreen mode Exit fullscreen mode

The shape you want is that summary line ending in 0 tampered. If even one session had been altered, it would show up as TAMPERED (...) and the exit code would be 1 — which means you can put this in a script. And note that the chain has been intact since the moment the import wrote it. There is no blessing step afterwards.

The trail format itself is vendor-neutral — an adapter reads each tool's native logs, and from there everything lands in the same shape under a common schema. And as you just saw, verification is a local operation against the .basou/ on your disk.

The sessions ahead

Everything so far was about the past. The future is even easier.

Start the morning with basou orient — "where was I?" answered from the record. Day to day, basou refresh is the one-shot that ingests new sessions and refreshes every view. When a design decision lands, basou decision capture; before you step away, basou note "pick up here next". And if you want a session recorded live from the start, basou run claude-code wraps the whole process.

If you use Codex: basou import codex --all — same shape, same kind of chain.

What this is not

Not a dashboard SaaS. Nothing leaves your machine. No LLM calls at runtime. The trail itself lives entirely inside .basou/, and walking away is as easy as deleting that one directory. If you want to browse locally, there's basou view (bound to 127.0.0.1 only) — that too is optional.

Wrapping up

That was the five minutes: install and init, one import, handoff generate, verify. From here the trail keeps growing next to your code, views can be deleted and resurrected from the source of truth, and tampering gets caught by a machine.

The quickstart and the CLI reference have the rest. If something snags on 0.x, issues are welcome.

Top comments (0)