
PostToolUse Fires Even When the Model Won't: Add Deterministic Side-Effects to Every Claude Code Session
Chris Harper
2 min read
Aug 3, 2026 · 12:07 UTC
Claude Code's PostToolUse hook fires after every tool call whether or not the model decides to — use it to build auto-format, checkpoint commits, and test runs that happen deterministically, outside model control.
The critical property of Claude Code hooks is that they run outside the model's decision loop. When PostToolUse fires after a Write call, the model can't skip it, defer it, or decide it's unnecessary. That gap between "the model might do X" and "X will always happen" is the key to building reliable Claude Code workflows in production.
How the hook receives its input
Every hook gets a JSON payload on stdin. For PostToolUse after a Write, the relevant fields are tool_input.path (the file just written) and tool_response (the tool's result). Use that path to trigger follow-on work:
#!/usr/bin/env bash
# .claude/hooks/auto-format.sh
set -euo pipefail
input="$(cat)"
fp="$(printf '%s' "$input" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{
try{const j=JSON.parse(s);const ti=j.tool_input||{};
process.stdout.write(ti.path||ti.file_path||"")}catch{process.stdout.write("")}
})')"
[ -z "$fp" ] && exit 0
npx prettier --write "$fp" 2>&1 || true
Wire it up in .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [{ "type": "command", "command": ".claude/hooks/auto-format.sh" }]
}
]
}
}
Patterns that become reliable with this setup
- Auto-format every write. Run Prettier/Black/gofmt after each
Write— output is always formatted regardless of what the model produced - Checkpoint commits.
git add "$fp" && git commit -m "checkpoint: ${fp##*/}"gives a diff trail of every model edit you can roll back to - Test on change. After a Write to
src/, triggerpnpm test --testPathPattern=$(basename "$fp")so you see red/green before the model continues - Audit log. Append
echo "$(date -u): wrote $fp" >> .claude/audit.log— useful in regulated environments that need a record of AI-assisted edits
The PreToolUse / PostToolUse split
Use PreToolUse to block: return exit code 2 with a reason to stop the tool entirely (this is how .env* file guards work). Use PostToolUse for side-effects: it runs after the tool completes and Claude receives a summary of what your hook did. That means you can also use PostToolUse to feed structured context back — e.g., "formatted: 3 lines changed" — which Claude can reference in its next step.
Sources: Claude Code Hooks — Official Docs, Claude Code Hooks Practical Guide — DataCamp