Commit Messages Now Have Two Readers: You and the Next Agent Session
Source: simonwillison
Simon Willison’s guide on using git with coding agents establishes the right frame: git is safety infrastructure for agentic workflows, not just historical record-keeping. The practical advice, commit frequently, use worktrees for parallel sessions, review diffs before staging, is worth following. But there is a dimension worth examining more carefully: git history now operates as a two-way channel. What you write into commits affects what you get back out in future agent sessions, and that changes the purpose of several practices you probably already have.
The Purpose of a Commit Changes
When you write code without agents, commits serve one direction: forward in time, toward future readers. A commit message explains a decision. The diff shows what changed. The history answers “what happened and why” for whoever inherits the codebase.
Coding agents add a second reader and a second direction. Before a session starts, an agent can read git log to understand what was done recently, which areas of the codebase have been active, and what decisions were made in prior sessions. The commit history you wrote in the last session is context the next agent session will read before doing anything.
“fix bug” tells a future agent nothing. “fix null pointer in auth middleware when session token is expired” tells it not to look at that code path as a source of a related issue, to recognize that session token expiry is a known complexity in that module, and to understand what was recently changed there. The difference is not primarily about the commit message being legible to humans; it is about whether it is useful to an automated consumer that has no other way to understand the codebase’s recent history.
# What context does an agent have before it starts?
git log --oneline -20 # recent decisions
git diff main...HEAD --stat # current branch surface area
git show <hash> # what a specific commit did and why
Willison’s “atom everything” principle asks you to commit after each discrete unit of agent work, partly for this reason. Frequent, well-described commits build up a dense context layer the next session can draw on. The payoff is not just rollback fidelity; it is that you are building the working memory your next agent invocation will use.
Rollback Fidelity Is Not Readability
The reason to commit frequently during agent sessions differs from the usual reason to commit. Normal commit discipline is about producing a history that reads well: accumulate related changes, commit them as a logical unit, write a message that explains the intent. The commit serves a future human reader.
Committing for rollback fidelity works differently. You commit at every stable state because the smaller the commit, the more precisely you can recover from an agent mistake. If an agent session produces one large commit, a regression means reverting everything. If it produces ten small commits, you can bisect, keep the parts that were correct, and revert only what went wrong.
These goals conflict. Fine-grained agent commits are useful for recovery but create a noisy history. The standard resolution is to commit frequently during the session and squash before merging:
# During the session: commit every logical step
git commit -m "[agent] add JWT validation to middleware"
git commit -m "[agent] extract token validator to its own function"
git commit -m "[agent] fix off-by-one in token rotation"
# Before merging: squash into a single coherent commit
git merge --squash agent/auth-refactor
git commit -m "refactor: JWT refresh token handling with expiry validation"
Aider handles this at the tool level by auto-committing every change with a generated aider: prefix, keeping the working tree clean between operations. The assumption is you will squash before shipping. Claude Code does not commit on its own by default; the commit stays a human action. You can override this in a CLAUDE.md file with explicit instructions, but the default preserves the commit as a deliberate human checkpoint. Neither approach is wrong, but they reflect different beliefs about where in the workflow the readability versus fidelity tradeoff should be resolved.
The Enforcement Gap
Most teams using Claude Code encode git policy in a CLAUDE.md file: do not commit directly to main, do not force push, do not amend published commits. This works in practice, but it is worth being precise about what kind of working it is.
CLAUDE.md is advisory. The agent reads it at the start of each session, incorporates the instructions into its context, and follows them with high but not absolute reliability. In long sessions, attention to instructions read early can degrade. For a hard constraint with a high cost per violation, CLAUDE.md alone leaves a gap.
Git pre-commit hooks close that gap categorically. They run regardless of whether a human or an agent triggered the commit, and they cannot be bypassed by contextual attention drift:
# .git/hooks/pre-commit
#!/bin/bash
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$current_branch" = "main" ]; then
echo "Direct commits to main are not allowed."
exit 1
fi
The same logic applies to secret scanning, type checking, or any invariant you want maintained regardless of what generated the commit:
#!/bin/bash
git secrets --scan # block accidental secret commits
npx tsc --noEmit 2>&1 # type errors before committing
npm run lint --silent # linting
There is a subtle failure mode worth knowing: when a pre-commit hook fails, the commit did not happen. If you fix the issue and run git commit --amend, you are amending the previous successful commit, not retrying the failed one. Claude Code’s safety protocol explicitly addresses this case; on hook failure, fix the issue, restage, and create a new commit. This is the correct recovery path, but it requires knowing that --amend is the wrong one, since most developers reach for it by reflex after fixing a pre-commit failure.
The practical division: use CLAUDE.md for conventions and preferences where occasional deviation is tolerable. Use hooks for invariants where any single violation has a real cost.
The Clean Tree Precondition
Every pattern above depends on starting agent sessions from a clean working tree. Before invoking an agent on any non-trivial task, commit or stash whatever you are currently editing. After the session, git diff HEAD shows exactly what the agent changed, with no noise from prior edits mixed in.
git add -A && git commit -m "wip: checkpoint before agent session"
# ... run agent ...
git diff HEAD # exactly what the agent changed
git diff HEAD --stat # files touched and line counts
If the agent produces something wrong, git reset --hard HEAD returns to the pre-session state without any manual identification of which changes were yours versus the agent’s. The recovery is unambiguous.
This practice exists in most style guides for ordinary development. With agents, it shifts from good hygiene to a functional requirement. The cost of starting dirty used to be a messier diff. With agents in the loop, the cost is losing the ability to cleanly attribute or recover changes, which is substantially harder to address after the fact.
What the Two-Way Channel Means in Practice
The shift in git’s role is real but not dramatic. The same tools, git log, git diff, git worktree, git reset, work identically. What changes is the motivation behind each practice and the payoff timeline.
Frequent commits serve rollback fidelity, not just readability. Precise commit messages serve the next agent session alongside the next human reader. Pre-commit hooks enforce the constraints that advisory config files cannot. Clean trees make diff-based review possible, not merely easier.
Willison’s guide is correct that git is already the right mechanism for agentic workflows. The deeper point is that it is the right mechanism partly because it was always designed as a communication channel, and agents are now among the things it communicates with.