Introducing jjq, a local merge queue for jj
jjq is a CLI tool that lets you, or more likely, a coding agent, submit a change to be considered for landing on the trunk of a jj repository. Think of it like a local version of a merge queue from a hosted forge.
jjq will automatically test to see if the change can be cleanly rebased or merged on the trunk commit, and run a user-configurable check command (think: a command you might run in CI, like make test, cargo clippy) on the candidate new trunk revision. If the check passes, the new revision becomes the new trunk, and the bookmark (usually main, etc.) is moved to it.
paulsmith/jjq - local merge queue for jj
jjq helps with the problem of having many agents working on new features, bug fixes, and refactorings in parallel, and attempting to land their changes concurrently on the trunk.
We assume that the trunk is the head of a timeline in the repo that is "always green" or "blessed", where the tests always pass and there are no lints to fix.
If we want to keep trunk that way and reduce the potential chaos, but also maximize the amount of work that can be done on our repository at the same time, we could use a regular merge queue like the ones that hosted forges provide.
Merge queues help prevent PRs from becoming too stale to merge, because the trunk has moved since it was submitted. There are likely merge conflicts and/or failing tests that would result were the PR to be simply merged or rebased onto the trunk.
We could use the forge merge queues, but there are reasons we might not want to. The main reason is that in an environment with a lot of parallel development happening at once, the chance of a failed merge (either due to conflicts or failing tests) is higher. We want to quickly resolve this situation, ideally by reusing the context window of the agent responsible for the change.
You may have reasons for not using a centralized or hosted forge, or be using one without a merge queue feature. In any case, jjq can help increase velocity of agents on a repository locally.
One nice thing about Git is its support for pre-commit hooks. jj doesn't support pre-commit hooks. You could think of jjq as an advisory pre-submit hook.
How to use jjq
Get jjq via Homebrew brew install paulsmith/tap/jjq or from a GitHub release.
To use jjq in a jj repository, you initialize it one time with jjq init. This will prompt you for a trunk bookmark name (defaults to main), your check command (eg., make test), and optionally a landing strategy, whether you prefer rebasing (the default) or merging.
When a change is ready to be submitted, use jjq push <REVSET>.
$ jj log
@ nylxuwxp paulsmith@pobox.com 2026-02-24 11:22:43 5cf2bdd8
│ file b
│ ○ oqrsoyvx paulsmith@pobox.com 2026-02-24 11:22:21 e5fcf50c
├─╯ file a
○ mtswnruy paulsmith@pobox.com 2026-02-24 11:21:31 main 28f2b146
│ init
~
$ jjq push oqrsoyvx
jjq: revision 'oqrsoyvx' queued at 1 (trunk: main in /private/tmp/foo.LWAvuB)
You can inspect the state of the merge queue with jjq status.
$ jjq status
jjq: Queued:
1: oqrsoyvxpptw file a
When you are ready to process the queue, use jjq run.
$ jjq run
jjq: processing queue item 1 (rebase strategy)
jjq: rebased 1 to main (now at oqrsoyvxpptw)
$ jj log
@ nylxuwxp paulsmith@pobox.com 2026-02-24 11:22:43 5cf2bdd8
│ file b
│ ○ oqrsoyvx paulsmith@pobox.com 2026-02-24 11:30:28 main dbf49b34
├─╯ file a
○ mtswnruy paulsmith@pobox.com 2026-02-24 11:21:31 28f2b146
│ init
~
Notice that the main bookmark has been moved.
For more on using jjq, type jjq --help, or jjq quickstart (the latter is usually enough for agents to use it effectively).
jj
I've long felt that jj is a nice fit with agentic coding patterns. jj's core model, different from Git, that the working copy is always committed, and revisions are mutable and easily reordered, rebased, and modified, aligns well with the slightly chaotic world of agents making spikes, refactoring, writing plans, trying things that wind up in dead ends and backtracking, etc. I never worry that an agent is going to make a hash of my repo, because I (or it) can always undo or inspect the op log and restore the repo to a known-good state.
jj workspaces are especially convenient for agents. Any task they do, I have them perform in a new workspace. Workspaces, which are isolated working copies in separate directories, inherit all the advantages of jj, avoiding the frustrations and limitation of Git worktrees.
A productive workflow for me is to have agents going in their own workspaces and attempting to land their changes onto the trunk via jjq. Then either I or a supervisor agent can run the queue, periodically or continually, and feel confident that if the change lands it won't break the build. If it fails, we can quickly reassign the agent to address the cause and resubmit.
Origins
jjq has become a solid building block in my engineering when using agents. It started life as a Bash script, driving the jj CLI and parsing its output.
(Incidentally, jj's templating feature for CLI output essentially provides you with your own custom stable API, emitting just the output your tool needs for its job. This is a nice alternative to using the jj library, which is not an option for non-Rust programs, and even for Rust programs, since the CLI handles some things on your behalf like reading user configuration.)
After using jjq as a script for a while and refining the interface (mostly by paving the cow paths that the agents made), I had Claude Code port the script to Rust.
I did an intermediate step, which was writing a RFC-style specification document, treating jjq like an abstract interface, spelling out what features and behaviors conforming implementations may or must support. The spec is still in the repo history. I'm not sure, not having a control group, whether this made the port more or less successful, or faster to implement. (There was the cost of writing the spec in the first place.)
Given how good agents are at porting from one language to another, it perhaps was a waste of time to write up an RFC-style spec. But I've wanted to explore trying some evals with the idea on future projects.