Case studies

exhale in a day

The first piece of software made for Imps went from idea to a public release in under eight hours. Agents wrote the code, and the new Ruby gem had to meet its own Contract before it shipped.

Huff, the purple imp, purses his lips and blows two orange cards away. He keeps a third card in his hand, and his little orange robot sits at his feet on its leash.

Rule 12 says every pull request breathes. The inhale is the sprawl, where the agent builds until the feature takes shape. The exhale folds what it learned back into what already exists, before the pull request merges.

exhale is a toolkit for that second half. It's a gem, which is what Ruby calls a published library, and its first check is exhale dry. The check fails a pull request while the codebase holds duplicated code, and it names the original each copy should fold into.

Obie wrote it on October 2, 2026, in one Claude Code session. He sent Claude the first prompt at 11:36 a.m. Mountain time. Version 0.1.0 was published on RubyGems at 7:20 that evening, 7 hours and 44 minutes later. The quoted passages on this page are most of Obie's messages to Claude Code in that session, so that you can learn how to apply Impatient Programming rules yourself.

What each check caught

Eight checks ran over the day, in this order. Obie asked the questions in the first row, and agents and tools ran the rest.

Check What it caught
Obie's questions The Contract's layout, the whole-codebase sweep, determinism, and the Contract coming first
Sweeping real code A sweep that ran past two minutes, and exhale's own duplicate
Adversarial review 19 proven defects, each with a script
Writing the Contract 5 live defects, 2 of them in new speedups
Mutation testing About 85 gaps in the tests, hidden behind a ten-second cap
Codex review 20 findings, 9 of them most serious
Mutation testing, second pass 57 survivors in the code that fixed those findings
Mutation testing on CI One survivor that lived only on Linux, in dead code

Why exhale exists

it's for the breathe out, contraction phase of what I recently called Breathe In, Breathe Out. Every PR must include a full removal of any technical debt introduced by the expansion

Agents duplicate by default. They find a shape that works, and they copy it. When pull requests merge without a person reading every diff, the copy reaches the main branch unless a machine stops it, and every session after that reads the copy and makes another.

Obie's first request was a duplication checker built for Ruby that combined Uncle Bob's dryer with Ryan Davis's flay. When the name exhale came up, he said it sounded like more than a duplication checker.

exhale makes me think this is going to be some sort of comprehensive solution not just the flay part!

And just like that, exhale became the toolkit for the whole breath out, with one check for each kind of cleanup. exhale dry shipped that day. The second check followed five days later, in version 0.2.

Design: Obie's steering before any code

Five of Obie's messages changed the architecture, and four of them arrived before any code existed. None of the five contained code.

Hmm, it looks like these checks we're implementing are not totally deterministic at the moment. We need to make sure there's no chance that what passes one day will fail another, or vice versa

The first draft judged each pull request against the commit it had branched from, and its scores shifted as the codebase grew, so every merge nudged every score. Both decisions changed before any code was written, and the same commit now gets the same verdict on any machine.

true Imps will do the whole codebase everytime they touch it

The initial explorations checked only the code a pull request touched, but after Obie's guidance every run sweeps the whole codebase. The main branch has to pass the same gate, so anything a pull request trips over is its own doing. An app with years of history won't come up clean on its first run, and a flag lets it gate on new duplication alone until the main branch is clean.

Two more of the five settled where deliberate duplication gets declared. (Basically, how the tool keeps track of exceptions to the rule.) Sometimes duplication is a design decision, and each one now goes into the Contract next to its reason. An exception clause that names code which no longer exists fails the build.

Inhale: building the first version

Claude wrote the matching engine while three subagents built the parts that feed it. Thirteen minutes after the subagents launched, 108 tests were green.

The first real codebase it swept was Agora, ZAR's in-house agent orchestration platform, which is currently maintained almost entirely by autonomous agents. The initial exhale run, the first on a real production application codebase, took around two minutes. Obie was not happy with that and after a couple of improvement cycles, the performance was down to fifteen seconds. Across Agora's 1,862 files the sweep found dozens of cases worth further investigation, including exact copies. One particular form fragment had been copied 22 times. Agora is only about 9 months old, any Rails app with a few years behind it will probably yield hundreds of cases of duplication worth removing.

After Obie commented that we should run exhale on its own codebase, Claude found one duplicate, in two of its own error classes. They were folded into a shared base class before opening the initial pull request.

The inhale ended at 2:45 p.m. as pull request #1, with 111 tests passing. Its description carried a checklist of everything the exhale still owed.

Exhale: cleaning up in the same pull request

The exhale happened in the same pull request, between 2:49 and 5:29 that afternoon.

Adversarial review

Two reviewing agents went first, with instructions to break the code. They proved 19 defects, and each one came with a script that reproduces it. A typo in the command name had exited as a pass, which would have switched the gate off in CI, the automated checks that run on every change. One git setting was enough to flip a commit's verdict.

A Contract for exhale itself

Claude began fixing the 19 defects one at a time. Then Obie sent his fifth major prompt.

How about we apply what we've been developing under the banner of Impatient Programming aka IMP regarding the need to establish the Contract, to this project?

The release paused while exhale got a Contract of its own, with 64 obligations. An obligation is one plain sentence about what the tool must do, paired with a test that names it. A build task fails while any obligation has no test, and it prints the coverage, which is rule 29.

After that every fix started in the Contract, as rule 28 asks. The missing obligation went in with a test proven to fail against the old code, and only then did the code change. Twenty-two tests were proven red that way. Five of them caught defects that were still live, including two in speedups Claude had written within the hour.

Mutation testing

Mutation testing breaks the code on purpose, one small change at a time, and counts how many of the broken versions the tests catch. Each broken version is called a mutant, and a mutant no test catches is a survivor. The first run scored 96 percent with 34 survivors.

That score was wrong. Mutineer, the mutation tool, gave every mutant ten seconds. A survivor takes the longest to judge, because every test that touches it has to finish, so the survivors were the ones timing out, and a timeout didn't count against the score. Fifty-one more of them had been hiding behind the cap.

An agent was also editing a test file during that run. A half-written test fails on everything, so it took credit for catching mutants it had never tested.

With the cap raised and nobody editing tests, the score dropped to 91 percent. Some survivors pointed at behavior that lived only in the code, and one of those became obligation 64. By 4:55 p.m. no survivor was left.

Mutineer belongs to David Teren. While the mutation runs went on, Claude built a kill matrix for it, a report of which test catches which mutant. Rule 26 uses one to find the tests that catch nothing. Obie decided the work should go upstream. David merged the pull request three days later, and the kill matrix shipped in Mutineer 1.5.

Review by a second model

Just before five the build was green, with every obligation tested and no survivors. Then a different model read the code. OpenAI's Codex spent eleven minutes on it, read-only, and came back with 20 more defects. Nine were ranked most serious. In one of them, three identical methods sat under a Contract clause that covered two, and the gate passed without judging the third.

The obligations were tightened first. Five agents then worked in parallel, each writing a failing test and fixing the code under it. When mutation testing ran again it found 57 survivors, in the code that had just fixed Codex's findings.

Release

Obie left the console at 5:22 p.m. with one final instruction, to land the release once it was finished. Finished meant every check passing on the final code, which is rule 27.

On the Linux machines that run CI, one survivor turned up that had died on macOS. It sat in a method nothing called anymore, so the fix was to delete the method and its two tests. At 7:19 p.m. every check was green. A minute later the pull request merged and the release workflow published exhale 0.1.0 to RubyGems.

The day in numbers

7h 44m
from the first message to RubyGems
64
obligations in the Contract, each with a test that names it
369
tests in the suite that shipped
1,154
mutants, with no survivors
39
review findings fixed, 19 from the Opus reviewers and 20 from Codex
20
Claude subagents, plus 3 Codex runs
2 to 1
lines of test code to lines of library code
$129.56
on Claude Code's session report at the end of the day

Tests catch 1,068 of the 1,154 mutants. The other 86 are listed in a file, each with the reason no test can catch it. The work was done well within the capacity of a paid Anthropic subscription, but back of the napkin math says it consumed about $120 in Opus 5.5 tokens.

Lessons from the day

  • Prompt about the design before any code exists. Five of Obie's messages changed the architecture, and four of them came before the first line of code.
  • Record every finding as an obligation with a test. A fix made in place can quietly regress. An obligation with a test that names it fails the build when that happens.
  • Hold performance work to the Contract. The 57 late survivors came from code written to make exhale faster or safer, and so did two of the five live defects.
  • Check the mutation tool before trusting its score. The first score was wrong in two ways. A timeout cap hid survivors, and a test file changed in the middle of the run.
  • Give the code to a second model. Codex needed eleven minutes to do adversarial review. It read code that had already passed two Opus reviews and a 100 percent mutation score, and it found nine serious defects.

Version 0.2: exhale complexity

Five days later exhale got its second check. exhale complexity fails a pull request that leaves a Ruby method harder to follow than it found it. Agents add a branch here and a nested condition there, and each change looks reasonable in its own diff. A month later the method is a maze nobody chose to build.

The check scores every Ruby method and Rails DSL body with G. Ann Campbell's Cognitive Complexity, once at the head of the pull request and once at its merge base. A method fails when its score rose and ended above the floor, which is 8 by default. Code that was already complex passes until somebody makes it worse, so a legacy app can turn the gate on without a cleanup sprint first. Some methods have to stay complex, like a parser's dispatch. Those get a ceiling in the Contract next to the reason, and a ceiling that's no longer needed fails the build, the same as a stale duplication clause.

It was built the way 0.1 was. The Contract came first, with 15 new obligations. An independent review found two bugs: a define_method nested inside a method went unscored, and a ceiling set under the floor could fail the method it was meant to keep. Each one got a failing test before its fix. The mutation gate killed all 340 mutants in the new code and listed 6 more as equivalent, each with its reason. exhale now has 82 obligations, every one with a test, and CI runs both checks on exhale itself. Obie opened the pull request on the evening of October 6, and 0.2.0 reached RubyGems the next morning.

Mutineer 1.5 shipped in between. Along with the kill matrix it added a timeout setting and better test selection for statements that span several lines. David sent exhale a pull request of his own that moved its mutation gate to 1.5. It deleted the patch that had raised the ten-second cap, and 8 hand-written ignore entries weren't needed anymore. Read the 0.2.0 release notes for the full list.

Known limits

exhale has known limits, and the README lists them. Matching on structure can't see the same idea written in a different structure. The threshold for how alike two pieces of code must be before they count as copies is still a starting value, waiting to be calibrated on real apps. A third check, for leaked guards, is planned.

Try exhale on your own app

# Gemfile
gem "exhale", "~> 0.2", require: false
bundle binstubs exhale
bin/exhale dry --introduced-only
bin/exhale complexity --base origin/main

The source is at github.com/tools4imps/exhale-ruby and the gem is at rubygems.org/gems/exhale. exhale and its sibling qualm are on the Tools page.