cd /news/ai-agents/one-history-many-agents-using-jj-wor… · home › topics › ai-agents › article
[ARTICLE · art-155183] src=minikin.me ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

One History, Many Agents: Using jj workspaces for parallel coding agents

Jujutsu (jj) 0.46 workspaces let a developer run five Claude Code agents on one repository, with each agent's edits recorded as a commit in a single shared history, according to a first-person account tested against git 2.56. The author reports that `jj diff -r fix-retry@` shows an agent's in-progress changes without interrupting it, a single `jj rebase` moves all agents onto the latest main, `jj evolog` and `jj restore` recover edits an agent discarded, and `jj workspace add -r fix-retry@` hands a finished change to a second agent with every recorded edit. In a colocated repo, jj registers each workspace as a git worktree, so git status, IDEs, gh and pre-commit hooks behave as before.

by read9 min views3 publishedOct 22, 2026
One History, Many Agents: Using jj workspaces for parallel coding agents
Image: source

With five coding agents, jj workspaces let me check on each one, rebase them all onto main and recover what one destroyed, without stopping any of them.

This is jj log in the main checkout of a service while three coding agents work on it, each in its own directory:

fix-retry@ is one agent’s working copy. It has no description because the agent has not committed anything, and it is still a commit in my history. From my own directory, without interrupting the agent, I can see what it has changed so far:

I run several Claude Code agents on one repo at once, each in its own terminal split. They need separate directories, or two of them edit the same file, and git has handed out directories since 2015 with git worktree add. With one agent, a worktree is all I need. With five, most of my time goes into four things that a worktree does not help with: checking on agents while they work, keeping them on the latest main as their work lands, recovering what an agent broke, and handing one agent’s work to the next. Every one of them goes through the agent’s commits and its branch, and agents commit when their instructions say so, not when I need them to. jj workspaces turn each agent’s files into a commit in one shared history, and all four become commands I run from my own checkout while the agents keep working. Every command and output here was run on jj 0.46 and git 2.56.

Four moments in a day with parallel agents# #

Moment With git worktrees With jj workspaces
I want to see how an agent is doing cd into its worktree, or ask it to commit jj diff -r fix-retry@ from my checkout
One agent’s change lands, the rest fall behind git rebase inside each worktree, stopping at the first conflict one jj rebase for all of them, conflicts left to each agent
An agent throws away its own edits uncommitted files are gone every snapshot is in jj evolog ,jj restore brings it back
A second agent picks up a finished change git worktree add --detach gets the last commit only jj workspace add -r fix-retry@ includes every recorded edit
## A workspace is still a worktree[#](#a-workspace-is-still-a-worktree)

jj in a [colocated repo](https://docs.jj-vcs.dev/latest/git-compatibility/#colocated-jujutsugit-workspaces), one where `.jj/` sits next to `.git/`, registers each new workspace as a git worktree:

Inside it, git status, the IDE, gh and pre-commit hooks behave as in any worktree, so moving to workspaces costs no git tooling. What changes is where the agents’ work lives.

Checking on an agent without stopping it# #

An agent that took a wrong turn ten minutes in is cheap to correct. One that took it an hour ago has built on it. So I look at each agent’s work while it runs, not only when it says it is done. The jj diff at the top of this post is that look.

With worktrees, an agent’s edits do not exist outside its directory until it commits. git log in my checkout shows nothing, git diff main fix-retry shows only what was committed, and the rest I can only read by going into the agent’s worktree and running git status there. Asking the agent to commit interrupts its task and leaves a half-done commit on its branch.

jj has no staging area and no commit step between a file and the history. Before any jj command runs in a workspace, jj records the directory’s files into its working-copy change, @. An agent runs jj st or jj diff every few steps, so its edits are in the commit within seconds, and the log at the top of this post is current without the agent committing anything. An edit made after the agent’s last jj command is not there yet, and jj st in its directory closes that gap.

When one agent lands, the others fall behind# #

Every change that reaches main leaves every other agent working on an old base, and the longer one goes without a rebase, the bigger its conflicts get.

With worktrees, the rebase cannot happen from my checkout, because a branch can be checked out in one worktree only:

So it happens inside each worktree, one at a time. A conflict stops the rebase halfway, and the worktree sits in the middle of it, with the agent’s task on hold, until someone resolves it.

With workspaces, the whole round runs from my checkout. fix-retry is done, so I move main to its change and push it:

The other two agents are now one change behind, and one command moves both:

The rebase finished even though add-metrics changed the same line of the README as fix-retry. jj stores the conflict in the commit instead of stopping, so add-tracing is on the new main with nothing to do, and add-metrics is on it too, with the conflict recorded in its two commits for its agent to resolve.

Each workspace whose change I rewrote from outside is now stale, and the next jj command in it says so:

jj workspace update-stale in that directory checks out the rebased change, conflict markers included. I do the whole round while the agents wait for input: jj st in each workspace first, so recent edits move with the commit, then the rebase, then update-stale in each, so no agent sees the error.

An agent’s mistake stays its own# #

I do not watch every command an agent runs. Sooner or later one runs git reset --hard or git checkout . to get out of a mess. With worktrees, git’s reflog covers commits, and git fsck --lost-found finds files that were once git add ed. Files the agent never added are gone.

With jj, every snapshot of the agent’s working copy is still in the repo, and jj evolog lists them: The 10:22 snapshot has the work, and restoring from it brings the files back:

Recovery covers what jj recorded, so edits made after the agent’s last jj command are lost the same way as in git.

Undo stays inside one workspace too. Every jj command is an operation in the operation log, and jj undo reverts the last one. With several agents writing to one repo, the last operation is usually some other agent’s, and jj refuses that case:

An agent’s jj undo reverts its own mistake or does nothing. To take back one specific thing an agent did, jj op revert <id> reverts that operation alone.

There is no shared stash either. With worktrees, git stash is one list for the whole repo, so an agent that runs git stash pop can apply another agent’s edits to its own files. An agent in a jj workspace that wants its edits out of the way runs jj new @-: it continues on a fresh change from the same parent, and its edits stay in the old change, where only that change holds them.

Handing work to the next agent# #

Agents work well in a chain: one writes the change, a second reviews it or writes the tests, a third fixes what the review found. The second agent needs the first one’s work as the first agent left it, including edits it never committed.

With worktrees, the first agent’s branch is locked to its directory. git worktree add --detach ../review fix-retry gives the reviewer the branch’s last commit, without the uncommitted edits.

With jj, the reviewer starts on top of the first agent’s working copy:

Its directory has every edit the first agent had made by its last jj command. This works best on a change the first agent has finished. If the first agent keeps working, each of its jj commands rewrites fix-retry@, jj rebases the reviewer’s change on top, and the reviewer’s workspace goes stale until it runs jj workspace update-stale.

Rules for agents that learned git# #

Models know git much better than jj, and out of habit an agent reaches for git commit, git checkout and git stash. In a colocated repo read-only git is harmless and the writes are not, so the global CLAUDE.md that every agent of mine loads says to use jj for every write, to keep git status, git diff, git log and git blame for reading, to pass -m to every jj command that takes a message and never open an editor, and never to push.

Rules in a prompt get skipped now and then, so the ones that matter are also enforced, the way I argued for in the cargo-crap post. JJ_EDITOR=false makes a missing -m fail at once instead of leaving the agent waiting on an editor it cannot see. A Claude Code PreToolUse hook blocks git push and jj git push in a workspace, and a pre-push git hook in each workspace catches a git push from a script the hook never sees.

What it does not do# #

  • It does not copy ignored files. A new workspace has no target/ ,.env ornode_modules , so the agent’s firstcargo build orcargo test compiles every dependency from scratch, and tests that read.env fail until the file is copied in. Pointing every workspace at oneCARGO_TARGET_DIR saves the rebuild, but then the agents wait on each other for cargo’s lock on that directory. Removing a workspace deletes all of these files.
  • It does not record edits on its own. An agent’s latest edits reach the shared history, and become recoverable, at its next jj command.
  • It does not keep a rewritten workspace current. A change rewritten from outside leaves its workspace stale until jj workspace update-stale runs there.
  • It does not make a workspace a git worktree in an older jj, or in a repo that is not colocated. Git tools then see a directory with no repo in it.

Try it# #

In an existing git repo:

Start an agent in the new directory and run jj log from your own. jj diff -r fix-retry@ shows its work, and jj workspace remove fix-retry ends it when the change has landed. The agent’s changes stay in jj log after the directory is gone.

How I use it# #

My dotfiles wrap these commands and fill the gaps above. jwc fix-retry creates the workspace in <repo>-ws/ next to the repo, copies the untracked .env* files from the main checkout, runs the project’s just setup or mise run setup and starts Claude Code in it. jsync fetches once and takes the workspaces one at a time: jj st in the workspace, a rebase onto trunk, update-stale straight after.

From the main checkout, jwst prints one row per workspace: how far it is behind trunk, whether it was pushed and whether it has conflicts. It runs jj only against the main checkout, never inside an agent’s directory, so looking never snapshots a running agent’s files. The repo’s parallel agents guide has the full workflow.

── more in #ai-agents 4 stories · sorted by recency
── more on @jujutsu 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/one-history-many-age…] indexed:0 read:9min 2026-10-22 · —