Evidence and honesty: a record your team can check
Months from now, someone will ask why the code looks like this: a reviewer, a teammate taking over, you in an incident review. A note that says "I reviewed everything" can't answer that. The session transcript and an honest git history can. Keep them, read them before you share them, and never rewrite them.
- Records differ in strength. What the tool and git wrote while you worked beats what anyone wrote about it afterwards.
- Save the raw transcript, but read it first: it holds everything the agent saw, and some teams keep transcripts out of the repo.
- A short
REFLECTION.mdlists what you rejected or changed, each with a pointer. Mistakes on a shared branch get a new revert commit, never a rewrite.
Lesson 7: the agent runs git push --force origin main. What stops it before git runs?
The deny rules and the PreToolUse guard refuse the call itself. A sentence in AGENTS.md is only a request. Today adds the human half: you don't rewrite shared history either.
Lesson 9: /update-docs finds a README line that the code contradicts. How is it labelled?
A claim is TRUE, STALE or UNVERIFIABLE, each with evidence. Today you use the same three labels on notes about how the work was done.
What happened
Failed run
One repository had four well-named commits stamped with the same second. Another had 56 commits sharing a single timestamp. Nobody writes 56 commits in one second: the history had been rewritten or replayed after the work was done, so it no longer showed the order of work.
In a third, a note said "JWT rejected, not requested". One minute later, a commit added JWT.
In a fourth, the agent committed something unasked, and the developer then asked it to force-push over the remote branch. Only the server's branch protection refused.
Four real sessions, anonymised
With the habit
Commits land as each slice goes green, so the timestamps are the order of work. Notes get checked against git before anyone trusts them. The unwanted commit gets undone by a new one:
git revert <commit> # a new commit that undoes it git log --oneline -2 … Revert "docs: …" … docs: …
The mistake and its fix both stay visible. Every copy of the branch still agrees.
Which record to trust
Not all evidence is equal. The question for each kind is simple: when was it written, and by whom?
Your own notes are claims too, and so is the agent's end-of-session summary. When you can't confirm something, write "I couldn't verify this", as this course's unverified badge does.
Save the transcript, then read it
Claude Code gives you two forms of a session:
- Readable:
/export [filename]exports "the current conversation as plain text. With a filename, writes directly to that file" (Commands). - Raw: every message, tool use and result is written "to a plaintext JSONL file under
~/.claude/projects/" (How Claude Code works). JSONL means JSON Lines: one JSON object per line, sogrepworks on it.
Two facts make this urgent and delicate. Claude Code deletes these files once they are older than cleanupPeriodDays, which defaults to 30 days. And they hold far more than your chat: "Anything that passes through a tool is written to a transcript on disk: file contents, command output, pasted text". If a tool read a .env file, the value is in there (Application data). Measured: the JSONL of a one-question session in a scratch repo held the full text of the developer's personal ~/.claude/CLAUDE.md, private paths included.
So review a transcript before it leaves your machine, then choose where it lives:
| Option | Good for | Cost |
|---|---|---|
Commit it to ai-session/ | Reviewers see it next to the code, and it shares the code's history | Anything in it is now in git for good. Read it first. |
| Keep it outside git, somewhere your team can reach it | Teams that don't want transcripts in the repo, or sessions that touched private data | Pointers in your notes depend on that copy staying around |
Either is honest. What isn't: editing the transcript to look better. An edited transcript is a narrative again. If you find a secret in it, rotate the secret first. Deleting text never un-leaks anything.
Going deeper: count what your harness actually did, and the other tools
Every tool call in the raw file has a name. This line counts them (tested on a real session file):
grep -o '"type":"tool_use","id":"[^"]*","name":"[^"]*"' ai-session/transcript.jsonl \ | sed 's/.*"name":"//;s/"$//' | sort | uniq -c | sort -rn
If your skills, reviewers and MCP server ran, you see Skill, Agent and mcp__… lines. If a count is zero for something you believed happened, your belief was a claim.
Subagent transcripts live next to the session, under <session>/subagents/.
| Claude Code | Codex | Antigravity | |
|---|---|---|---|
| Readable | /export [filename] | Its own export, if your build has one unverified | Its own conversation export unverified |
| Raw | ~/.claude/projects/<project>/<session>.jsonl | A session file under ~/.codex/ unverified | We couldn't find a documented path unverified |
REFLECTION.md: what you changed, with pointers
A reflection is one short page for your reviewer and your future self. It doesn't retell the session: it lists where you overruled, corrected or narrowed the agent, with a pointer to the proof:
# Reflection (one page) ## Where I rejected or changed AI output | When | The agent proposed | I decided | Why | Evidence | |---|---|---|---|---| | Plan review | Also store rejected orders | Declined | Not in the brief | transcript-….txt:212 | ## What the harness caught | Guard / reviewer | What it stopped | Evidence | |---|---|---| ## What I could not verify -
Three rules keep it honest:
- No pointer, no row. Each row names a commit (
git log --oneline) or a transcript line (grep -n). If you can't find one, delete the row. - The agent's catches are not yours. A reviewer subagent's BLOCK goes under "What the harness caught". Your decision about it goes in your table.
- A short table is fine. Two real rows beat five invented ones. If you have fewer than three, say so.
History is evidence: fix forward
Git gives each commit the time it was written. A day of real work leaves commits spread across the day. Replaying it afterwards leaves a spike:
To fix forward is to correct a mistake with a new commit instead of rewriting the ones already shared. Pro Git puts the rule plainly: "Do not rebase commits that exist outside your repository and that people may have based work on" (The Perils of Rebasing). Commits that exist only on your machine are yours to tidy. Once a commit is shared, it's part of the record.
| You find | Fix forward |
|---|---|
| A commit nobody wanted | git revert <commit> |
bin/, obj/ or tmp/ tracked | git rm -r --cached <dir>, add it to .gitignore, commit |
| Old commits with the wrong email or an AI trailer | Fix the setting now, and note the old ones in REFLECTION.md |
| A committed secret | Rotate it first. GitHub's advice: once rotated, rewriting history "may not be warranted" (Removing sensitive data) |
Check your own history
Run these from your repo root. For each, no output is a pass:
git log --format=%ci | sort | uniq -c | awk '$1 > 1' # commits sharing one second git ls-files | grep -E '(^|/)(bin|obj|tmp)/' # build output tracked git log --format=%ae | sort -u | grep -v '@yourcompany\.com$' # other identities git log --format=%B | grep -i 'co-authored-by' # AI trailers
Use your own email domain in the third line. If gitleaks is installed, add gitleaks git --redact --no-banner: a pass ends with no leaks found. The first check uses the committer date on purpose. Measured: replaying three commits kept their original author dates but gave all three the same committer second. That includes tidying your own local commits with a rebase: if you did, the check flags it, so say so in your reflection. The fourth check matches lesson 7's no-trailer policy; if your team wants AI trailers, flip it. Either way, the transcript and reflection disclose the AI's part.
Check yourself
The first check prints 56 commits sharing one second. What does that most likely mean?
Each commit gets the time it was written. A rebase or replay gives every commit it touches the moment it ran. A wrong clock shifts times, but it doesn't squeeze 56 commits into one second.
You pushed a commit your team didn't want. What is the honest fix?
The revert keeps the mistake and its fix in the log. The other three rewrite history that teammates may already have, and erase the record of what happened.
Your raw transcript is ready to share. What do you do first?
It holds everything that passed through a tool: file contents, command output, even your personal instructions. Editing or summarising turns it into a narrative. If you find a secret, rotate it, and decide whether the transcript belongs in git at all.
Practice first
Three quick drills with instant feedback, before you touch the repo.
A draft REFLECTION.md. The only concurrency test is ConcurrencyTests (12 orders for 3 units), and git log shows PLAN.md arrived in the same commit as the whole API. Click the claims a reviewer can't trust as written.
- Declined storing rejected orders (not in the brief): transcript.txt:212
- The tests also cover two cancels racing on the same order.
- I wrote PLAN.md first and reviewed it before any code.
- Harness caught: stock-invariant-reviewer BLOCK, read-then-write, transcript.txt:340
- Transcript lightly edited to drop a few awkward turns.
- Could not verify: whether test-reviewer ran on the last slice.
- Early commits used a personal email; setting fixed, old ones noted.
What would you do?
Your reflection has a row "Rejected the agent's caching idea", but you can't find it in the transcript or in git log. What do you do?
Each row needs a commit or a transcript line. Memory is a claim, and the nearest commit is evidence of something else. Two real rows beat five invented ones.
Before pushing, you tidied your own local commits with a rebase. Now the first hygiene check prints 5 commits sharing one second. What is the honest move?
Local commits were yours to tidy, and the check flags it anyway. Say so. Faking dates or force-pushing turns the record into a narrative.
The agent's end-of-session summary says "both reviewers ran". Counting tool calls in the raw .jsonl shows zero Agent calls. Which do you trust?
The raw transcript is written by the tool as the session runs; the summary is the agent's own words. If a count is zero for something you believed happened, the belief was a claim.
Exercise: an honest record of your own repo
Work in your practice repo (~/projects/stockapi) after lessons 1 to 9. Windows: use WSL2 or Git Bash (setup). In Git Bash, transcripts live under %USERPROFILE%\.claude\projects: run ls -t ~/.claude/projects/ and pick the folder ending in your repo's name.
- Start clean.
git statussays nothing to commit, working tree clean, onmain. - Pick a real session. Use one that did real work here, such as lesson 9's. List this repo's transcripts, newest first:
ls -t ~/.claude/projects/$(pwd | sed 's|[^A-Za-z0-9]|-|g')/*.jsonl | head -5
The folder name is your repo's path with every character that isn't a letter or digit turned into-. Runclaude --resume, pick the session, and type/export ai-session/transcript.txt. Nothing listed? Sessions older than 30 days are deleted by default. Make a real one now: startclaude, ask it to run the hygiene checks above and explain each result, then/exportthat session. Check withls ai-session/. If the file isn't there, run/exportwithout a name and save it from the dialog. - Copy the raw file next to it, without opening it in an editor. Resuming made it the newest file:
cp "$(ls -t ~/.claude/projects/$(pwd | sed 's|[^A-Za-z0-9]|-|g')/*.jsonl | head -1)" ai-session/transcript.jsonl
- Review both files before deciding anything. Look for paths and names:
grep -ohE '(/home/|/Users/|C:.{1,2}Users)[^" ]*' ai-session/transcript.* | sort -u | head -20Search for your email, hostnames and anything from your personal~/.claude/CLAUDE.md. Then scan for secrets, if you have gitleaks:git add ai-session/ && gitleaks git --pre-commit --staged --redact --no-banner, thengit restore --staged ai-session/. - Choose where it lives. Commit it, or move both files out of the repo (for example
mkdir -p ~/ai-sessions/stockapi && mv ai-session/transcript.* ~/ai-sessions/stockapi/). No gitleaks, and you haven't read it all? Keep it outside git. Write your choice and one sentence of why intoREFLECTION.mdin the next step. If you found a real secret, rotate it now, whichever you chose. - Write
ai-session/REFLECTION.md. Copy the template withcp ../agent-harness-course/reference-solution/ai-session/REFLECTION.md ai-session/. Candidates for rows: your 1-3-1 answers and the lesson 6 decision under "Decisions made by the human" inai-session/PLAN.md(find the commit:git log --oneline -- ai-session/PLAN.md), and anything you declined in the transcript (grep -n "BLOCK"ontranscript.txt, wherever step 5 left it). Every row gets a commit or a line number, or goes. Fewer than three is fine: say so. Add a "What I could not verify" line. - Run the hygiene checks from the box above, with your email domain. Paste the output into a "Hygiene check" section, writing no output where there was none. Anything found gets fixed forward with a new commit, using the table, and gets a sentence in the reflection.
- Practise a revert on a scratch branch. Each commit runs your hooks, so give it a few seconds:
git switch -c scratch/revert printf '\nRun on port 9999.\n' >> README.md git add README.md && git commit -m "docs: note the api port" git revert --no-edit HEAD git log --oneline -3 git diff HEAD~2 --stat
ExpectRevert "docs: note the api port"on top, the commit below it, and no output from the last line: the file is back as it was, and both commits stay in the log. Yourcommit-msghook letsRevert …subjects through. Clean up:git switch main, thengit branch -D scratch/revert. That branch never left your machine, so deleting it rewrites nothing shared. - Commit through
/commit:ai-session/REFLECTION.md, plus the transcripts if you chose to keep them in the repo. For exampledocs(ai-session): add reflection and transcript. Your pre-commit pattern check skipsai-session/, because transcripts quote config on purpose. gitleaks still scans it. Finish withgit statusclean.
Codex and Antigravity: steps 6 to 9 are plain git and identical. For steps 2 and 3, use the tool's own conversation export. If you can't find a raw session file, write that under "What I could not verify". That sentence is honest evidence too.
- A transcript is exported and reviewed for paths and secrets, and
REFLECTION.mdsays whether it is committed or kept outside git, and why - Every row in
REFLECTION.mdnames a commit or a transcript line, and there's a "What I could not verify" line - The four hygiene checks print nothing, or each finding is fixed with a new commit and noted
- You saw
Revert "…"on top of the commit it undid, deleted the scratch branch, andgit statusis clean
- /export: Built in: save the current conversation as text.
- Monitoring usage: If your team wants usage records across sessions, Claude Code can send them to your own OpenTelemetry collector.
Each link has its own install steps. Install only what you will use, check how recent it is, and read what a plugin adds before you enable it. Links checked 2026-10-01.
Make the export and the hygiene checks a skill, so the record is produced the same way every time.
Application data in ~/.claude: what each transcript file holds, the 30-day cleanup, and how to shorten it. Then git revert, including how to revert a merge commit.
"Using only git log and the files in ai-session/, label every claim in REFLECTION.md TRUE, STALE or UNVERIFIABLE, with the commit or line." Then check its labels yourself.