← Back to all projects

Case study

lanes

  • Node.js
  • GitHub Actions
  • Claude Code

The problem

I build with several Claude Code sessions at once. Each is fast, and none of them should be trusted with main, which deploys. Nobody watches at night. So the question lanes answers is narrow: how can AI sessions ship to main unattended without ever breaking it?

The answer is to make GitHub’s required checks the only gatekeeper. No session can merge by being persuasive; a change merges only when the checks say so.

How a lane ships

A lane is one fresh Claude session for one GitHub issue, in its own worktree. It reads a task contract, writes the failing test first, then the code, and opens a pull request. Fresh reviewer agents read the diff: test-hunter always, and the security, UI and architecture reviewers when the files call for them. Then lanes/gate decides, and the merge queue checks the merged result once more.

Goals

  • Watch a clean lane reach main with nobody watching.
  • Send a sloppy task form, then complete it.
  • Let a reviewer find a bug, and push the fix.
  • Touch the rules, and approve the PR yourself.
  • Merge another PR first, and watch the queue re-check.
Task issuethe formContract checkissue-contractLanefresh sessionPull requestCloses #482Reviewerstest-hunterGatelanes/gateMerge queuere-checksmaindeployedTask issuethe formContract checkissue-contractLanefresh sessionPull requestCloses #482Reviewerstest-hunterGatelanes/gateMerge queuere-checksmaindeployed

A task issue: Goal, Acceptance criteria, Interface contract, Scope, Blocked by, Tier.

Each switch in the figure is a real rule, and every status line is the gate’s own text.

What it refuses

The gate fails closed: an unread input, an unknown permission or a list that might be cut short all count as “no”. A verdict covers only the commit it names, so a lane can’t fix a bug and keep its old approval. The files that decide the rules themselves (the workflows, the config) always wait for my review.

It is honest about what it can’t do. It stops honest mistakes and strangers, not a lane that turns hostile (USING.md), and it doesn’t certify the code it merges (SECURITY.md).

Results

lanes was built with itself: 365 lane PRs merged into the lanes repository between September 27 and October 18. It then ran satisfactory-dash, where 22 lane PRs merged in four days.

A measurement of 76 lanes showed what a lane costs: from a median of 2.4M tokens for changes under 50 lines to 17.1M for 500 to 1,000, with reviewers’ serious findings steady at about 0.2 per 100 changed lines whatever the size (lane size and cost).

Limits and trade-offs

lanes is not a general answer, and its own history says so.

Conway’s law. Lanes talk to each other only through issues, and each sees only its own files, so the system was built in file-sized slices. Changes that needed a holistic view were split, then queued one after another anyway, and I had to merge issues back together by hand (v0.1.0 snapshot). The tool’s structure shaped the work’s structure. One ~200-line change became four serial lanes costing about 15M tokens, against 6 to 8M as one. I changed the rule: issues are now split by size, about 100 to 300 changed lines, not by module (decision).

A fixed cost per lane. Every lane pays to read its context and run its reviewers. Under 50 changed lines, that costs four to five times more per line. Small, tightly coupled work is cheaper in one session.

Guards as a patch chain. Filtering what a session types into a shell can always be dodged; each fix closed the gap the last review found, and the chain had no natural end (snapshot). That is why the real gate judges results on GitHub, not intentions in a terminal.

One machine. The queue and every lane run on my computer. When it sleeps, nothing new starts or gets fixed (recorded, deferred).

Not hands-off. Approvals, launches and upgrades stay with me by design. An early mode that let lanes act as me was retired because it introduced bugs of its own (retired).

When not to use it. This site. It is small, static and mine alone, and its CI and my reviews are enough, so I chose not to install lanes here.

More detail: the parts of a lane

How it was built

lanes was built through lanes: its own changes went through its own gate, which is how most of the trade-offs above were found. It is open source on GitHub.