Stage 10 of 11

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.

12 min read 40 min exercise Plaque: Honest Log
In 30 seconds
Recall

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?

stronger → weaker 1Raw transcript (.jsonl, /export)✓ what was asked, run and seen, in order✗ that anyone read it · it can hold secrets 2Git history✓ what changed, by whom, in what order✗ why · and it can be replayed afterwards 3Files in the repo✓ what exists right now✗ when it arrived, or what it steered 4Notes, README, reflections✓ why, in someone’s own words✗ anything, until checked against 1 and 2 Checking one claima note in the repo says“JWT rejected, not requested” check it against gitgit log, one minute latera commit adds JWTSTALE · git contradicts itLabel each claim TRUE, STALE orUNVERIFIABLE, as in lesson 9.

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:

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:

OptionGood forCost
Commit it to ai-session/Reviewers see it next to the code, and it shares the code's historyAnything in it is now in git for good. Read it first.
Keep it outside git, somewhere your team can reach itTeams that don't want transcripts in the repo, or sessions that touched private dataPointers 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 CodeCodexAntigravity
Readable/export [filename]Its own export, if your build has one unverifiedIts own conversation export unverified
Raw~/.claude/projects/<project>/<session>.jsonlA session file under ~/.codex/ unverifiedWe 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:

ai-session/REFLECTION.md (one row filled in, as an example)
# 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:

  1. 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.
  2. 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.
  3. 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:

Honestone commit per slice09:0012:0015:0018:00 Replayedwritten afterwards09:0012:0015:0018:0056 commits, one timestamp$ git log --format=%ci | sort | uniq -c | awk … 56 ‹the same second› A mistake reaches the shared branch Fix forwardABXthe mistake Rgit revert X✓ X and its undo both stay in the log✓ every copy of the branch still agrees Force-push it awayABXbranch protectioncan refuse it✗ teammates who pulled X no longer match✗ the record of the mistake is gone

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 findFix forward
A commit nobody wantedgit revert <commit>
bin/, obj/ or tmp/ trackedgit rm -r --cached <dir>, add it to .gitignore, commit
Old commits with the wrong email or an AI trailerFix the setting now, and note the old ones in REFLECTION.md
A committed secretRotate 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:

hygiene checks (no output = 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.

  1. Declined storing rejected orders (not in the brief): transcript.txt:212
  2. The tests also cover two cancels racing on the same order.
  3. I wrote PLAN.md first and reviewed it before any code.
  4. Harness caught: stock-invariant-reviewer BLOCK, read-then-write, transcript.txt:340
  5. Transcript lightly edited to drop a few awkward turns.
  6. Could not verify: whether test-reviewer ran on the last slice.
  7. 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

Exercise · about 40 min · you add: a reviewed transcript, ai-session/REFLECTION.md, and one practised revert

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.

  1. Start clean. git status says nothing to commit, working tree clean, on main.
  2. 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 -. Run claude --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: start claude, ask it to run the hygiene checks above and explain each result, then /export that session. Check with ls ai-session/. If the file isn't there, run /export without a name and save it from the dialog.
  3. 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
  4. 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 -20
    Search 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, then git restore --staged ai-session/.
  5. 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 into REFLECTION.md in the next step. If you found a real secret, rotate it now, whichever you chose.
  6. Write ai-session/REFLECTION.md. Copy the template with cp ../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" in ai-session/PLAN.md (find the commit: git log --oneline -- ai-session/PLAN.md), and anything you declined in the transcript (grep -n "BLOCK" on transcript.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.
  7. 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.
  8. 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
    Expect Revert "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. Your commit-msg hook lets Revert … subjects through. Clean up: git switch main, then git branch -D scratch/revert. That branch never left your machine, so deleting it rewrites nothing shared.
  9. Commit through /commit: ai-session/REFLECTION.md, plus the transcripts if you chose to keep them in the repo. For example docs(ai-session): add reflection and transcript. Your pre-commit pattern check skips ai-session/, because transcripts quote config on purpose. gitleaks still scans it. Finish with git status clean.

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.

Done when: tick all four to earn the Honest Log plaque
Worth installing or reading

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.

Harness it

Make the export and the hygiene checks a skill, so the record is produced the same way every time.

Read next

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.

Ask your agent

"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.