Track and analyze the delta between "the code an AI agent first wrote" and "what you actually committed after human review". Every review is recorded automatically: the diff is archived, each change is AI-classified (necessary fix / design gap / overengineering / convention...), and attributed (hand-edited vs. reworked-by-agent-under-direction). Later, export an aggregate report showing which problems recur most and which could have been avoided via prompt changes, CLAUDE.md entries, lint, or tests.
Single-file Python CLI, zero third-party dependencies. Requires git and the claude CLI.
SessionStart hook ──── join the worktree's chain (task base = HEAD)
Stop hook ──────────── snapshot the worktree when the agent stops and the
tree changed (first snapshot = "agent's first version")
UserPromptSubmit hook ─ detect hand edits between agent turns, snapshot them
post-commit hook ───── settle on every commit: diff, archive, AI-classify,
then re-anchor a fresh chain at the new HEAD
SessionEnd hook ─────── sweep stale unfinished chains
Tracking is per worktree, not per session: snapshots capture the whole worktree, so all uncommitted work between two commits — agent turns from any Claude session, hand edits, manual marks — is one stream and one chain. A chain can span several Claude sessions; feedback is collected from each session's transcript from the moment the agent's first version exists.
Snapshots are real commit objects created with plumbing commands
(commit-tree + update-ref) under refs/reviewtax/:
- not on any branch — invisible to
git log/git status/ GitHub; - never touch your staging area (a temp index is used);
- protected from
git gcbecause a ref points at them; - plumbing never fires git hooks, so snapshots can never trigger a settle;
- once archived as patch text they can be cleaned up, leaving no trace.
Every commit settles the current chain into one record and starts the next
cycle, so records line up 1:1 with commits that contain agent work (commits
with no agent snapshots since the last one settle nothing). If the agent
stopped and a commit landed with no feedback and no hand edits in between,
the record is flagged pass_through (not reviewed) and counted separately.
# 1. Install the Claude Code hooks (user-level, merged into
# <claude_dir>/settings.json with a backup)
python3 reviewtax.py setup
# 2. Opt in each repo you want tracked (git config flag + post-commit hook)
cd /path/to/repo && python3 reviewtax.py enable
# Optional: a shim for convenience
ln -s "$(pwd)/reviewtax.py" ~/.local/bin/reviewtax
# Optional: an agent skill so AI agents know how to drive the CLI
# (SKILL.md at the repo root; the repo itself acts as the skill directory)
ln -s "$(pwd)" ~/.claude/skills/reviewtaxThe hooks silently no-op (a few ms) in repos without enable, and never
interfere with a Claude session — all errors are swallowed and logged.
Restart any running Claude Code sessions to pick up the hooks.
Just work normally: the agent writes code, you review (send feedback or
hand-edit), the agent commits. Collection is fully passive. One habit worth
keeping: when the agent needs more than one turn to finish, run
reviewtax mark at the moment it's done — see the next section.
reviewtax status # per-worktree review state, recent records
reviewtax report # records settled since the previous report
reviewtax report --all # full history instead of the increment
reviewtax report --since 2026-07-01 --out report.md
reviewtax report --no-ai # stats tables only, no model callsPassive tracking anchors "the agent's first version" at the first snapshot
after the task base. That is right for one-shot tasks: everything after the
first version is review. But when the agent needs several turns — or several
sessions — to finish the code, the later turns are still development, not
review, and would be miscounted as review delta. Declare the boundary
yourself: the moment the agent is done and you start reviewing, run
reviewtax mark.
A record is built from two points, and mark controls both:
- Review start —
marksnapshots the current worktree as "the agent's first version", discarding any earlier snapshots in the cycle. Everything from this moment to the nextgit commit(feedback-driven rework and hand edits) is the review delta, and messages from now on count as review feedback. - Task base — the state before the agent touched anything. This is an
existing commit, not a snapshot: by default, the commit the current cycle
started from (normally the last commit), and base → mark is recorded as
the agent's work. If the agent committed mid-task, the cycle was re-anchored
at that commit, so pass
--base <commit>to point the base back at the true task start.
reviewtax mark # review starts now
reviewtax mark --base <commit> # ...and agent work started from <commit>mark is also the repair tool when hooks missed the session or the automatic
anchor landed on the wrong turn.
# Fully retroactive (the agent's version is already a historical commit):
reviewtax record --base <base> --agent <agent-version> [--final HEAD] \
[--transcript x.jsonl]
# Re-run a failed/updated AI analysis for one record:
reviewtax analyze ~/.reviewtax/records/<repo>/<record-id>
reviewtax settle # trigger a settle manually (same as post-commit)
reviewtax disable # stop tracking this repo (hook becomes a no-op)
reviewtax sweep # remove chains idle beyond stale_days~/.reviewtax/ # override with REVIEWTAX_HOME
chains/<repo-key>.json # per-worktree in-flight chain state
records/<repo>/<record-id>/
meta.json # stats, tree hashes, pass_through flag
agent.patch # task base -> agent's first version
review.patch # agent's first version -> final commit
hand-edit.patch # hand-edit segments (for attribution)
feedback.md # your verbatim review feedback
analysis.json # AI classification results
reports/ # exported reports
logs/reviewtax.log # diagnostics
correctness (necessary fix) / design-gap (requirement never stated) /
churn (reverts or redoes what the review itself asked for earlier — a
direction change, not an agent defect) / overengineering /
simplification / convention / security / cosmetic. Each entry
carries evidence (code-level facts backing the judgment), a root cause,
and avoidable_by (prompt / claude_md / lint / test / none). The analyzer
runs inside the target repository so it can read the real code to verify its
conclusions instead of judging from diff hunks alone, and it must check the
feedback history before blaming the agent's first version for a change the
review itself directed.
All machine-specific settings live in ~/.reviewtax/config.json — the
repository itself contains no absolute paths or personal information.
Precedence: env var REVIEWTAX_<KEY> > config.json > default.
reviewtax config shows the effective values.
The data root is set via the REVIEWTAX_HOME env var (default ~/.reviewtax;
it can't live in the config file that sits inside it).
Note: setup / enable write your local interpreter and script paths into
<claude_dir>/settings.json and each repo's .git/hooks/post-commit. Those
files live outside this repository and are never committed; after moving the
script or switching machines, just re-run setup / enable.
{ "model": "sonnet", // model for analysis calls; "" = claude CLI default "no_ai": false, // disable all AI calls (record only) "claude_dir": "~/.claude", // Claude Code config dir (settings, transcripts) "patch_limit": 60000, // truncation for each patch fed to the analyzer "feedback_limit": 20000, // truncation for feedback fed to the analyzer "stale_days": 7 // sweep chains idle longer than this }