ilo: A Programming Language for AI Agents, Not Humans

The Same Two Commits in git, jj, and atomic

Here we will explore the same tiny experiment run three times: a toy Python project, two parallel changes appending different functions to the same file, a merge, a bad commit, and an undo. Once each in:

  • git, the control
  • jj (Jujutsu) 0.44, git-compatible, no staging area
  • atomic 0.14, a new “semantic change graph” system that markets itself directly at AI agents

I build ilo around the premise that when an agent is the primary user of a tool, the tool’s units should change: measure in tokens, expose structure the model can act on cheaply. atomic arrives at the same concepts independently, one layer down at version control, and I wanted to see whether they hold up in a working directory.

The experiment

The test is built around one situation: two agents append different functions to the end of the same file at the same time. One line of history adds mul to calc.py, the other adds div, both at the tail of the file. The edits have nothing to do with each other, but they occupy the same lines, which is the collision you get every time you fan agents out over one repository. After merging I committed something deliberately wrong and tried to undo it, because recovery from a bad commit is the move an unattended agent has to make without a human in the loop.

That last step matters for agents specifically. My Claude Code setup has a hook that blocks git reset --hard as a dangerous command, so an agent on my machine cannot use git’s most direct undo. Whatever the VCS offers instead is what the agent gets.

git: the merge stops the world

git merge feature/mul exited non-zero and left the repository in a conflicted state:

CONFLICT (content): Merge conflict in calc.py
Automatic merge failed; fix conflicts and then commit the result.

git status shows UU calc.py, the file has <<<<<<< markers in it, and nothing else can proceed until a human or agent edits the file and commits. This is the familiar case, and the cost to an agent is specific: the failure arrives as an exit code plus a mutated working tree, and the recovery procedure is stateful. Before that, git init had created master while my script assumed main, which cost me one failed run.

For the undo, with reset --hard blocked by my own tooling, the agent’s remaining move is git revert, which works but appends a correction commit rather than removing the mistake.

jj: the conflict is recorded and work continues

jj’s model is that the working copy is always a commit and conflicts are recorded inside commits rather than halting operations. When I rebased the div change to sit parallel to mul, jj completed the rebase and reported:

New conflicts appeared in 1 commits:
  nxwrszvo 87d1cee1 (conflict) add div

The log marks the commit with × and the repository stays fully usable. Merging the two heads with jj new produced a working copy in which the conflict was already resolved, both functions present. The conflicted commit still materialises markers if you ask for the file, but no command ever refused to run.

The undo is one command. jj undo reversed my bad commit, and jj op log showed every operation with its arguments, which is the audit trail I currently reconstruct by hand when checking what a subagent did to a branch.

One caution from the same session: I ran jj new with a description(...) revset that failed to resolve, jj printed an error, and my next edit quietly landed on the wrong parent. The history was linear when I intended parallel. An agent using jj needs to verify structure with jj log after any revset-addressed operation, because the failure does not stop the subsequent work.

atomic: no conflict happened

atomic organises work into views. I split feat-mul and feat-div off the dev view, made the same two appends, recorded each, then inserted both changes back into dev:

atomic insert UX3QFL63   # add mul
atomic insert NXDQRQ4Z   # add div

Both inserts succeeded, with no conflict markers and both functions in the file. atomic’s changes are patches in a graph rather than snapshots, so two changes that touch the same region but do not depend on each other commute. The result had one blank line missing between two functions, which I would want to understand before trusting it on real code, but the main result stands: the conflict git manufactured out of these two edits does not exist in atomic’s model.

The other thing that stood out is what atomic chooses to measure. Every record reports token counts alongside lines:

[dev 1/U6JSWUGD] init: calc lib
 2 files changed, +6 vertices, ~0 edges, 93 bytes
 7 lines (+7 -0 ~0)
 45 tokens (+45 -0 ~0)

The help text goes further: an agent command group for turn-level recording hooks, a session ledger with forking at turn boundaries, a sandbox command for copy-on-write working trees per concurrent agent. I have only read the help output for those, not exercised them, so I am reporting what the CLI lists rather than reviewing it.

Undo exists as atomic unrecord for the last change, and atomic restore refuses to discard uncommitted work without --force, a default I would rather hand an agent than reset --hard.

The same bet, one layer down

ilo measures programs in tokens because context is what an agent spends. atomic stamps token deltas on every change for the same reason. ilo treats code as structure the model manipulates rather than prose it rewrites; atomic treats a change as graph vertices rather than a text diff, which is exactly why my two appends merged cleanly. The session ledger and sandboxes in atomic’s help text map onto the trace-and-isolate plumbing I have built by hand around git worktrees for the ilo persona pipeline.

The costs are visible too. atomic’s store was 4.6MB for a four-change toy repository where git used 192KB and jj 304KB. The merged file had that whitespace glitch. Version 0.14 of a single-vendor tool is not where my repositories are going.

Where this lands for me: jj is usable now. jj git init --colocate drops it into an existing git repository, remotes and GitHub stay unchanged, and agents get conflicts that never block and an undo that is safe enough to leave unblocked by policy. atomic is the one I will re-run on a later release, because it is the first VCS I have run that starts from the same premise ilo does, and the merge behaviour shows the premise doing work.