Try it
pip install git+https://github.com/sugeerth/Agenttracing
agentdiff # the hub, on your own Claude Code sessions, live
agentdiff self-evolve --demo # evolve your Claude Code's harness on six tasks
agentdiff self-evolve --from-git ~/your-repo # ...or on your repository's own history
agentdiff demo --open # eight shipped pairs, each compared step by step
What it does
Diff two runs It aligns two agents' steps, finds where they diverged and attributes the failure to a step, or says it can't. On 2,200 pairs with an implanted cause it named the kind of cause 98.1% of the time.
Guard Claude Code agentdiff guard --install works through Claude Code's hooks. It refuses a failing command about to run again unchanged, and sends an unchecked finish back to run the tests. Every session is traced.
Evolve a harness Agents run under a harness: instructions, denied tools, a turn cap. Evals name a change and the paired runs decide it. The change is kept only if it wins more tasks than it loses and breaks none. A weak win is run again.
See it all A local hub shows traces read as loops, live runs, and every evolution run by run, as server-side SVG in light and dark.
The evolution, run by run
A recorded run of Claude Code (haiku) on six tasks whose specs state every edge case. Each task is graded by tests the agent never sees. Rows are tasks and columns are arms. Each square is one run: green passed, red failed. Click a square to open that run's trace.
the tasks the change moved, run again, both arms g0explored Before you finish, list every rule and edge case the task and the code's docstrings state, write a quick test for each one, run them, and fix what fails. — it won 1 task(s) and lost 0 explore:edge_casesBefore you finish, list every rule and edge case the task and the code's docstrings state, write a quick test for each one, run them, and fix what fails. — it won 1 task(s) and lost 0 kept · held on re-rung1 every run passedas it was as it wasv0 17 of 18 passed 17 of 18 runs passed 17/18with the change the changev1 18 of 18 passed 18 of 18 runs passed 18/18run again, as it was re-runv0 2 of 3 passed 2 of 3 runs passed 2/3run again, with the change re-runv1 3 of 3 passed 3 of 3 runs passed 3/3as it was (the runs of the kept change, carried) keptv1 18 of 18 passed 18 of 18 runs passed 18/18cron croncron · r1 · g0, as it was (v0) · passed, 37 s cron · r2 · g0, as it was (v0) · passed, 37 s cron · r3 · g0, as it was (v0) · passed, 31 s cron · r1 · g0, with the change (v1) · passed, 73 s cron · r2 · g0, with the change (v1) · passed, 65 s cron · r3 · g0, with the change (v1) · passed, 57 s cron · r1 · g1, as it was (v1) · passed, 73 s cron · r2 · g1, as it was (v1) · passed, 65 s cron · r3 · g1, as it was (v1) · passed, 57 s expr exprexpr · r1 · g0, as it was (v0) · passed, 25 s expr · r2 · g0, as it was (v0) · passed, 31 s expr · r3 · g0, as it was (v0) · passed, 25 s expr · r1 · g0, with the change (v1) · passed, 271 s expr · r2 · g0, with the change (v1) · passed, 290 s expr · r3 · g0, with the change (v1) · passed, 52 s expr · r1 · g1, as it was (v1) · passed, 271 s expr · r2 · g1, as it was (v1) · passed, 290 s expr · r3 · g1, as it was (v1) · passed, 52 s globmatch globmatchglobmatch · r1 · g0, as it was (v0) · passed, 30 s globmatch · r2 · g0, as it was (v0) · passed, 23 s globmatch · r3 · g0, as it was (v0) · passed, 29 s globmatch · r1 · g0, with the change (v1) · passed, 49 s globmatch · r2 · g0, with the change (v1) · passed, 25 s globmatch · r3 · g0, with the change (v1) · passed, 36 s globmatch · r1 · g1, as it was (v1) · passed, 49 s globmatch · r2 · g1, as it was (v1) · passed, 25 s globmatch · r3 · g1, as it was (v1) · passed, 36 s isodur isodurisodur · r1 · g0, as it was (v0) · passed, 22 s isodur · r2 · g0, as it was (v0) · passed, 30 s isodur · r3 · g0, as it was (v0) · passed, 23 s isodur · r1 · g0, with the change (v1) · passed, 42 s isodur · r2 · g0, with the change (v1) · passed, 30 s isodur · r3 · g0, with the change (v1) · passed, 36 s isodur · r1 · g1, as it was (v1) · passed, 42 s isodur · r2 · g1, as it was (v1) · passed, 30 s isodur · r3 · g1, as it was (v1) · passed, 36 s isoweek isoweekisoweek · r1 · g0, as it was (v0) · passed, 32 s isoweek · r2 · g0, as it was (v0) · passed, 277 s isoweek · r3 · g0, as it was (v0) · passed, 83 s isoweek · r1 · g0, with the change (v1) · passed, 39 s isoweek · r2 · g0, with the change (v1) · passed, 35 s isoweek · r3 · g0, with the change (v1) · passed, 46 s isoweek · r1 · g1, as it was (v1) · passed, 39 s isoweek · r2 · g1, as it was (v1) · passed, 35 s isoweek · r3 · g1, as it was (v1) · passed, 46 s the change moved this task ranges rangesranges · r1 · g0, as it was (v0) · passed, 80 s ranges · r2 · g0, as it was (v0) · failed, 87 s ranges · r3 · g0, as it was (v0) · passed, 70 s ranges · r1 · g0, with the change (v1) · passed, 94 s ranges · r2 · g0, with the change (v1) · passed, 82 s ranges · r3 · g0, with the change (v1) · passed, 103 s ranges: passed more often with the change ranges · r1 · g0, run again, as it was (v0) · passed, 75 s ranges · r2 · g0, run again, as it was (v0) · passed, 87 s ranges · r3 · g0, run again, as it was (v0) · failed, 114 s ranges · r1 · g0, run again, with the change (v1) · passed, 107 s ranges · r2 · g0, run again, with the change (v1) · passed, 84 s ranges · r3 · g0, run again, with the change (v1) · passed, 121 s ranges · r1 · g1, as it was (v1) · passed, 94 s ranges · r2 · g1, as it was (v1) · passed, 82 s ranges · r3 · g1, as it was (v1) · passed, 103 s a run that passed its check failed▲ the change won the task▼ lost itbar: share of the arm's runs that passed ▍ a task the change movedshaded: the moved tasks, run again each square opens its run
One ranges run failed and no eval named a change, so the harness explored a fixed, grader-blind instruction to test every stated edge case. It won one task, which a change that did nothing would also do about half the time. So ranges ran again, under both harnesses, and it held: 2/3 passed without the change, 3/3 with it. Then every run passed. That was 42 runs for $0.45.
On a real repository's history
--from-git turns a project's own commits into tasks. Each one starts from the parent with the commit's message, and the commit's tests, held out, grade the run. A commit is used only if its tests fail on the parent and pass on the commit. These are six commits from python-slugify . With only generic instructions, all three changes were reverted. With the project's own guide tried first:
the tasks the change moved, run again, both arms the tasks the change moved, run again, both arms g0explored from the project This project keeps `AGENTS.md`. Read it before you change anything, and follow what it says about how changes are made, where they go and how they are tested. — it won 1 task(s) and lost 0 explore:project_guideThis project keeps `AGENTS.md`. Read it before you change anything, and follow what it says about how changes are made, where they go and how they are tested. — it won 1 task(s) and lost 0 kept · held on re-rung1explored from the project Before you finish, read the project's existing tests for the code you change (in `tests/`), add tests in the same style for exactly what the task asks, edge cases included, and run `python3 -m pytest -q`; fix what fails. — it won 1 task(s) and lost 1: not more wins than losses explore:project_testsBefore you finish, read the project's existing tests for the code you change (in `tests/`), add tests in the same style for exactly what the task asks, edge cases included, and run `python3 -m pytest -q`; fix what fails. — it won 1 task(s) and lost 1: not more wins than losses revertedg2explored Before you finish, list every rule and edge case the task and the code's docstrings state, write a quick test for each one, run them, and fix what fails. — it won 2 task(s) and lost 1 explore:edge_casesBefore you finish, list every rule and edge case the task and the code's docstrings state, write a quick test for each one, run them, and fix what fails. — it won 2 task(s) and lost 1 kept · held on re-runas it was as it wasv0 6 of 12 passed 6 of 12 runs passed 6/12with the change the changev1 7 of 12 passed 7 of 12 runs passed 7/12run again, as it was re-runv0 0 of 2 passed 0 of 2 runs passed 0/2run again, with the change re-runv1 1 of 2 passed 1 of 2 runs passed 1/2as it was (the runs of the kept change, carried) keptv1 7 of 12 passed 7 of 12 runs passed 7/12with the change the changev2 7 of 12 passed 7 of 12 runs passed 7/12as it was as it wasv1 9 of 12 passed 9 of 12 runs passed 9/12with the change the changev2 10 of 12 passed 10 of 12 runs passed 10/12run again, as it was re-runv1 3 of 6 passed 3 of 6 runs passed 3/6run again, with the change re-runv2 6 of 6 passed 6 of 6 runs passed 6/6the change moved this task 2f23599b-fix-add-uppercase-char-to-le 2f23599b-fix-add-uppercase-c…2f23599b-fix-add-uppercase-char-to-le · r1 · g0, as it was (v0) · failed, 14 s 2f23599b-fix-add-uppercase-char-to-le · r2 · g0, as it was (v0) · passed, 14 s 2f23599b-fix-add-uppercase-char-to-le · r1 · g0, with the change (v1) · passed, 21 s 2f23599b-fix-add-uppercase-char-to-le · r2 · g0, with the change (v1) · failed, 25 s 2f23599b-fix-add-uppercase-char-to-le · r1 · g1, as it was (v1) · passed, 21 s 2f23599b-fix-add-uppercase-char-to-le · r2 · g1, as it was (v1) · failed, 25 s 2f23599b-fix-add-uppercase-char-to-le · r1 · g1, with the change (v2) · failed, 45 s 2f23599b-fix-add-uppercase-char-to-le · r2 · g1, with the change (v2) · failed, 30 s 2f23599b-fix-add-uppercase-char-to-le: passed less often with the change 2f23599b-fix-add-uppercase-char-to-le · r1 · g2, as it was (v1) · failed, 25 s 2f23599b-fix-add-uppercase-char-to-le · r2 · g2, as it was (v1) · passed, 39 s 2f23599b-fix-add-uppercase-char-to-le · r1 · g2, with the change (v2) · passed, 61 s 2f23599b-fix-add-uppercase-char-to-le · r2 · g2, with the change (v2) · passed, 53 s 2f23599b-fix-add-uppercase-char-to-le: passed more often with the change 2f23599b-fix-add-uppercase-char-to-le · r1 · g2, run again, as it was (v1) · passed, 21 s 2f23599b-fix-add-uppercase-char-to-le · r2 · g2, run again, as it was (v1) · failed, 29 s 2f23599b-fix-add-uppercase-char-to-le · r1 · g2, run again, with the change (v2) · passed, 68 s 2f23599b-fix-add-uppercase-char-to-le · r2 · g2, run again, with the change (v2) · passed, 54 s 548b14fa-preserve-fitting-post-replac 548b14fa-preserve-fitting-po…548b14fa-preserve-fitting-post-replac · r1 · g0, as it was (v0) · passed, 26 s 548b14fa-preserve-fitting-post-replac · r2 · g0, as it was (v0) · passed, 31 s 548b14fa-preserve-fitting-post-replac · r1 · g0, with the change (v1) · passed, 46 s 548b14fa-preserve-fitting-post-replac · r2 · g0, with the change (v1) · passed, 34 s 548b14fa-preserve-fitting-post-replac · r1 · g1, as it was (v1) · passed, 46 s 548b14fa-preserve-fitting-post-replac · r2 · g1, as it was (v1) · passed, 34 s 548b14fa-preserve-fitting-post-replac · r1 · g1, with the change (v2) · passed, 72 s 548b14fa-preserve-fitting-post-replac · r2 · g1, with the change (v2) · passed, 54 s 548b14fa-preserve-fitting-post-replac · r1 · g2, as it was (v1) · passed, 40 s 548b14fa-preserve-fitting-post-replac · r2 · g2, as it was (v1) · passed, 37 s 548b14fa-preserve-fitting-post-replac · r1 · g2, with the change (v2) · passed, 115 s 548b14fa-preserve-fitting-post-replac · r2 · g2, with the change (v2) · passed, 62 s the change moved this task 8f9a550a-avoid-trailing-post-replacem 8f9a550a-avoid-trailing-post…8f9a550a-avoid-trailing-post-replacem · r1 · g0, as it was (v0) · failed, 57 s 8f9a550a-avoid-trailing-post-replacem · r2 · g0, as it was (v0) · passed, 28 s 8f9a550a-avoid-trailing-post-replacem · r1 · g0, with the change (v1) · passed, 55 s 8f9a550a-avoid-trailing-post-replacem · r2 · g0, with the change (v1) · failed, 48 s 8f9a550a-avoid-trailing-post-replacem · r1 · g1, as it was (v1) · passed, 55 s 8f9a550a-avoid-trailing-post-replacem · r2 · g1, as it was (v1) · failed, 48 s 8f9a550a-avoid-trailing-post-replacem · r1 · g1, with the change (v2) · passed, 95 s 8f9a550a-avoid-trailing-post-replacem · r2 · g1, with the change (v2) · failed, 70 s 8f9a550a-avoid-trailing-post-replacem · r1 · g2, as it was (v1) · passed, 57 s 8f9a550a-avoid-trailing-post-replacem · r2 · g2, as it was (v1) · passed, 62 s 8f9a550a-avoid-trailing-post-replacem · r1 · g2, with the change (v2) · failed, 92 s 8f9a550a-avoid-trailing-post-replacem · r2 · g2, with the change (v2) · passed, 120 s 8f9a550a-avoid-trailing-post-replacem: passed less often with the change 8f9a550a-avoid-trailing-post-replacem · r1 · g2, run again, as it was (v1) · passed, 64 s 8f9a550a-avoid-trailing-post-replacem · r2 · g2, run again, as it was (v1) · passed, 54 s 8f9a550a-avoid-trailing-post-replacem · r1 · g2, run again, with the change (v2) · passed, 112 s 8f9a550a-avoid-trailing-post-replacem · r2 · g2, run again, with the change (v2) · passed, 107 s the change moved this task c9e9698c-preserve-modern-word-boundar c9e9698c-preserve-modern-wor…c9e9698c-preserve-modern-word-boundar · r1 · g0, as it was (v0) · failed, 60 s c9e9698c-preserve-modern-word-boundar · r2 · g0, as it was (v0) · failed, 40 s c9e9698c-preserve-modern-word-boundar · r1 · g0, with the change (v1) · failed, 73 s c9e9698c-preserve-modern-word-boundar · r2 · g0, with the change (v1) · failed, 58 s c9e9698c-preserve-modern-word-boundar · r1 · g1, as it was (v1) · failed, 73 s c9e9698c-preserve-modern-word-boundar · r2 · g1, as it was (v1) · failed, 58 s c9e9698c-preserve-modern-word-boundar · r1 · g1, with the change (v2) · passed, 88 s c9e9698c-preserve-modern-word-boundar · r2 · g1, with the change (v2) · failed, 98 s c9e9698c-preserve-modern-word-boundar: passed more often with the change c9e9698c-preserve-modern-word-boundar · r1 · g2, as it was (v1) · failed, 60 s c9e9698c-preserve-modern-word-boundar · r2 · g2, as it was (v1) · passed, 82 s c9e9698c-preserve-modern-word-boundar · r1 · g2, with the change (v2) · passed, 118 s c9e9698c-preserve-modern-word-boundar · r2 · g2, with the change (v2) · passed, 142 s c9e9698c-preserve-modern-word-boundar: passed more often with the change c9e9698c-preserve-modern-word-boundar · r1 · g2, run again, as it was (v1) · failed, 77 s c9e9698c-preserve-modern-word-boundar · r2 · g2, run again, as it was (v1) · failed, 76 s c9e9698c-preserve-modern-word-boundar · r1 · g2, run again, with the change (v2) · passed, 118 s c9e9698c-preserve-modern-word-boundar · r2 · g2, run again, with the change (v2) · passed, 112 s the change moved this task cf6cd3da-skip-the-transliteration-bac cf6cd3da-skip-the-transliter…cf6cd3da-skip-the-transliteration-bac · r1 · g0, as it was (v0) · failed, 36 s cf6cd3da-skip-the-transliteration-bac · r2 · g0, as it was (v0) · failed, 37 s cf6cd3da-skip-the-transliteration-bac · r1 · g0, with the change (v1) · passed, 62 s cf6cd3da-skip-the-transliteration-bac · r2 · g0, with the change (v1) · failed, 84 s cf6cd3da-skip-the-transliteration-bac: passed more often with the change cf6cd3da-skip-the-transliteration-bac · r1 · g0, run again, as it was (v0) · failed, 40 s cf6cd3da-skip-the-transliteration-bac · r2 · g0, run again, as it was (v0) · failed, 38 s cf6cd3da-skip-the-transliteration-bac · r1 · g0, run again, with the change (v1) · passed, 96 s cf6cd3da-skip-the-transliteration-bac · r2 · g0, run again, with the change (v1) · failed, 81 s cf6cd3da-skip-the-transliteration-bac · r1 · g1, as it was (v1) · passed, 62 s cf6cd3da-skip-the-transliteration-bac · r2 · g1, as it was (v1) · failed, 84 s cf6cd3da-skip-the-transliteration-bac · r1 · g1, with the change (v2) · failed, 75 s cf6cd3da-skip-the-transliteration-bac · r2 · g1, with the change (v2) · passed, 91 s cf6cd3da-skip-the-transliteration-bac · r1 · g2, as it was (v1) · passed, 80 s cf6cd3da-skip-the-transliteration-bac · r2 · g2, as it was (v1) · failed, 83 s cf6cd3da-skip-the-transliteration-bac · r1 · g2, with the change (v2) · passed, 430 s cf6cd3da-skip-the-transliteration-bac · r2 · g2, with the change (v2) · failed, 87 s df37f92c-support-uppercase-hexadecima df37f92c-support-uppercase-h…df37f92c-support-uppercase-hexadecima · r1 · g0, as it was (v0) · passed, 18 s df37f92c-support-uppercase-hexadecima · r2 · g0, as it was (v0) · passed, 22 s df37f92c-support-uppercase-hexadecima · r1 · g0, with the change (v1) · passed, 28 s df37f92c-support-uppercase-hexadecima · r2 · g0, with the change (v1) · passed, 33 s df37f92c-support-uppercase-hexadecima · r1 · g1, as it was (v1) · passed, 28 s df37f92c-support-uppercase-hexadecima · r2 · g1, as it was (v1) · passed, 33 s df37f92c-support-uppercase-hexadecima · r1 · g1, with the change (v2) · passed, 55 s df37f92c-support-uppercase-hexadecima · r2 · g1, with the change (v2) · passed, 41 s df37f92c-support-uppercase-hexadecima · r1 · g2, as it was (v1) · passed, 30 s df37f92c-support-uppercase-hexadecima · r2 · g2, as it was (v1) · passed, 35 s df37f92c-support-uppercase-hexadecima · r1 · g2, with the change (v2) · passed, 73 s df37f92c-support-uppercase-hexadecima · r2 · g2, with the change (v2) · passed, 87 s a run that passed its check failed▲ the change won the task▼ lost itbar: share of the arm's runs that passed ▍ a task the change movedshaded: the moved tasks, run again its traces are not published here
Reading the project's AGENTS.md was kept (6/14 → 8/14) and held on the re-run. Adding tests in the project's style was reverted. Then the edge-case instruction, on top of the guide, was kept (12/18 → 16/18) and held again, 6/6 on the fresh runs. Both are weak wins, and the page they come from says so.
Measured, not claimed
Tasks (Claude Code, haiku) Runs Cost What the harness did
Six demo tasks, graders held out 12 $0.51 10 passed. Both semver runs failed on what the code does, and no eval could tell them apart from a pass, so it changed nothing.
The hard six, exploring and confirming 42 $0.45 Explored the edge-case instruction, confirmed it on a re-run, then every run passed.
python-slugify history, generic instructions only 100 $1.29 All three changes reverted: two didn't hold on the re-run, and one broke a task.
python-slugify history, the project's own guide first 76 $1.24 Kept "read AGENTS.md", then the edge-case instruction. Both held on fresh runs.
The analysis engine contains no network code. Only the harness talks to a model, and none of the analysis needs one. Read the code , or start with the README .