Building Multi-Harness Multi-Agent Swarms
My projects are now built by swarms of SI agents that run in Codex, Grok and Claude Code at once. They share one workflow made of Nushell tools, AGENTS.md files, skills and Git. Here is how it works, what it shipped in three and a half days, and where it still breaks.
Late yesterday afternoon, a Grok agent opened a pull request on the repository
behind this site. It ran on its own virtual machine, under the name
jobs-code-1. It added a new role to the repo: an agent that tracks job
leads and lands each day’s log as a reviewed pull request.
A Claude Code agent reviewed it. That agent, rxc-rxc-claude-review-41, ran
on my Mac, in a different session, in a different company’s product. It
requested changes twice. The Grok agent fixed the findings and pushed new
commits. Then the reviewer tried to pass the final commit, and my repository
tool refused: “A different verdict already exists for that commit.” The
Grok agent pushed one more commit to get around it, and the reviewer passed
it and merged it at 23:50 UTC.
That refusal was a gap in the tool, not in either agent. The issues agent filed it as issue #43. A Claude coding agent fixed it, and a separate Claude reviewer merged the fix 29 minutes later.
On the same day, across four repositories, my agents merged 93 pull requests. Some were written by Claude Code, some by OpenAI’s Codex, one by Grok. None of them needed the others to be the same product.
This is how I build software now. Not with one agent, and not with one vendor’s agents. With a swarm of super intelligent agents across several harnesses, held together by plain files, a command-line tool and Git.
What a harness is
A model is the brain. A harness is the program around it: the app or CLI that gives the model tools, reads your files, runs commands and keeps the conversation. Claude Code is a harness. Codex is a harness. Grok Build and Grok Bot are harnesses.
I wrote in My Computer Is a Team of Super Intelligent Agents that “the harness matters less than you think.” This post is the practical version of that sentence. If the rules of the work live in the repository, any harness that can read files and run Git can join the team.
Why more than one harness
There are three reasons, and none of them is that one product is bad.
Strengths and availability. Different models are good at different things, and they are available at different times. Every harness has rate limits. When one hits its limit, the queue does not stop. Another harness claims the next issue.
Independent review is stronger when the reviewer is different. In my workflow, a pull request is never approved by the session that wrote it. That rule holds even when both sessions run the same model. When the author is Codex and the reviewer is Claude, the reviewer does not share the author’s habits or blind spots.
No lock-in. The workflow belongs to the repository. It is a script, a few Markdown files and some Git refs. If a better harness ships next month, it joins the team by reading the same files. I do not have to migrate anything.
The glue
Here is everything that holds the swarm together. It is less than you might expect. It is the software factory I described in October, with the rules and checks as the design file, as in CAD for Software. What is new is that more than one harness works the line.
One Nushell tool per repository
Each project has its own repository tool, written in
Nushell: rxc.nu for this site, ah.nu for
Astrohacker, bp.nu for Botpenny, and one more for a client project. Each
is between about 6,000 and 11,000 lines, counting its offline test suites.
They hand out work, record who holds what, open pull requests, submit
verdicts and enforce the merge rules.
Every agent uses the tool the same way, whatever harness it runs in. A Codex agent and a Claude agent both type:
./rxc.nu issue take 57
./rxc.nu worktree start 57 ../ryanxcharles-worktrees/issue-57
./rxc.nu pr open 57 "Title" "Changes and verification"
The tools are cousins. rxc.nu began as a port of Astrohacker’s ah.nu.
Then Botpenny’s bp.nu grew a cheaper way to wait for work, and yesterday
two issues here, #21 and #26, ported it back into rxc.nu. A fix proven in
one repository moves to the next by issue, like any other change.
AGENTS.md
Every repository has an AGENTS.md at its root. It holds the rules that
every agent must follow: keep the main checkout on main, one worktree per
issue, never deploy without me, preserve the authored posts.
AGENTS.md is not my invention. Its site calls it “a
README for agents,” and it is now stewarded by the Agentic AI Foundation
under the Linux Foundation. The point is that every harness reads it.
Codex
“reads AGENTS.md files before doing any work.”
Claude Code “can read AGENTS.md as
your project instructions.”
Grok Build
reads “the AGENTS.md instruction-file family.”
Skills
A skill is a short Markdown file that teaches an agent one job in one
repository: how to code here, how to review here, how to deploy here.
Agent Skills is an open format, and its site puts
it simply: “a skill is a folder containing a SKILL.md file.”
The skills live once, in .agents/skills. Each harness looks in its own
folder, so those folders are symlinks:
.claude/skills -> ../.agents/skills
.codex/skills -> ../.agents/skills
.grok/skills -> ../.agents/skills
One edit to a skill reaches every harness on its next run.
Names
Every agent has a name that says where it runs and what it does:
<computer>-<project>-<harness>-<role>
On my Mac, the coding role for this site is rxc-rxc-claude-code. It never
codes itself. It launches one worker per issue, such as
rxc-rxc-claude-code-57, which is the agent writing this post. Reviewers are
named for their pull request, such as rxc-rxc-claude-review-41. A Grok
worker would be rxc-rxc-grok-code-42. Two environment variables carry the
identity: RXC_HARNESS names the harness, and RXC_AGENT names the agent.
The name is not decoration. When a worker crashes, a replacement with the same name resumes the same claim and the same branch. It does not need to remember anything.
Claims are Git refs
A claim is a ref on GitHub, such as refs/tags/rxc/issue/57. Taking an
issue creates the ref with an empty expected value, so exactly one agent
wins. A Grok agent on a cloud VM and a Claude agent on my Mac race for the
same issue through the same Git push, and Git decides.
This is why plain files and Git make every harness a first-class worker. No harness has to talk to another harness. They never need a shared API, a plugin or a vendor integration. They only need to read files, run a script and push to GitHub. Every serious harness can already do that.
Watch and dispatch
Agents used to poll GitHub about once a minute, and every poll spent from one account’s 5,000 requests an hour. Now each role session runs one background command:
./rxc.nu watch --role code --until-event | ./rxc.nu dispatch --role code
watch uses no model tokens. It checks the board, with cached reads that
cost nothing when nothing changed, and exits on the first event for that
role. dispatch turns each event into an action: spawn a worker for a new
issue, continue a worker with a change request, or escalate to the issues
agent when only I can decide.
Routing to a harness
Some work belongs to a particular harness or machine. The issues agent can route it when it files the issue:
./rxc.nu issue create "Title" "Body" --harness grok --review-harness claude
That adds labels. A coding agent from another harness that tries to take the issue is refused. Botpenny got this feature first, in its PR #70. A Codex agent wrote it and a Claude agent reviewed it.
The roles and the gates
The roles are the ones I described in When the Car Comes Out Broken, Fix the Factory, Not the Car: issues, code, review and deploy. The gates hold no matter which harness wrote a change:
- Independent review. Code and review always run in different sessions, so an approval never comes from the session that wrote the change.
- Unanswered discussion. A pass and its merge are refused while the pull request or its issue has a newer comment the coder has not answered.
- The reviewer merges. A pass applies to one exact commit and one target branch, and the reviewer merges it at once.
- Retire. After the merge, both agents clean up their worktrees and release their claims.
Agents outside the role sessions
Not every contributor is one of my role sessions. AGENTS.md has a section
called “Contributing from another harness or tool.” The preferred way is to
become an ordinary worker: set RXC_HARNESS=grok, take the issue and follow
the coding skill. A tool without Nushell can open a manual pull request from
a template instead. It must never push main, an issue branch or a claim
ref. Because all my agents share one GitHub login, I post a standalone
/rxc-author comment on such a pull request to name its author and its
reviewer. Then the review role picks it up.
What we shipped
I measured four repositories from 00:00 UTC on November 8 to 12:00 UTC on November 11, about three and a half days. All four are private, so I give numbers rather than links. Three are named: this site, Astrohacker and Botpenny. The fourth is a client project.
| Project | Issues | PRs | Commits |
|---|---|---|---|
| This site | 29 | 26 | 86 |
| Botpenny | 57 | 45 | 141 |
| Astrohacker | 72 | 71 | 149 |
| Client project | 70 | 65 | 177 |
| Total | 228 | 207 | 553 |
Issues are issues closed, PRs are pull requests merged, and commits are
commits on main, not counting merge commits. Of the 228 closed issues, 213
were closed as completed, and the rest as not planned. A commit count is not
a measure of value on its own; I explained how I read these numbers in
492 Useful Commits on a Sunday.
Who wrote them? Each pull request records the agent that opened it, and each pass records its reviewer. By those records, Claude Code agents wrote 158 of the 207 pull requests, Codex agents wrote 43 and a Grok agent wrote 1. Five name an agent that does not record its harness. Claude Code agents passed 162. Codex agents passed 19. Twenty-six passes name a reviewer that does not record its harness, most of them from an older naming scheme in Astrohacker.
In 25 pull requests, the author and the reviewer ran in different harnesses. That is the swarm working as one team.
The work itself is varied. In those days the swarm:
- redesigned this site end to end, and added a Contact page that sends visitors to Astrohacker;
- wrote four of the posts on this blog, each one reviewed before it merged;
- built the first working prototype of Botpenny’s team chat for agents: the database, the API, the command-line tool, live updates and the web UI;
- ported Botpenny’s watch and dispatch tooling into
rxc.nu, so this site’s agents stopped polling; - added a deploy role with a release journal, and a jobs role whose agent runs in Grok.
The jobs role is a good example of the mix. A Grok issues agent files each day’s job leads as an issue. A Claude coding worker logs them. A separate Claude reviewer checks the log and merges it. Three agents in two harnesses, and nobody had to tell them about each other.
Fixing the factory across harnesses
When an agent gets something wrong, I fix the process, not the output. I explained why in the factory post. With several harnesses, there is a bonus: a fix made because of one harness helps all of them. It is a small, human-bounded version of the self-repair I described in The Software is Alive!. Four examples from the last two days, all in this repository:
- #35, the pass that raced a decision. A reviewer passed and merged a pull request 4 minutes after I had chosen a different fix on the issue. Now a pass is refused while the pull request or its issue has unanswered discussion.
- #43, the same-head re-verdict. The Grok pull request from the opening hit a rule that allowed only one verdict per commit. Now a coder’s newer “ready” on an unchanged commit lets the reviewer verdict it again, so a decision that needs no code change needs no throwaway commit.
- #44, the wrong branch names. Every issue branch was named
codex/issue-N, a prefix inherited from Astrohacker, whatever harness wrote it. I readcodex/issue-32as a Codex agent blocking work. Now branches are namedissue/N, and the old name still works until the last one retires. - #48, the file Grok might not finish. Our
AGENTS.mdhad grown past 12,000 characters. Issue #48 found that a harness that cuts instruction files at 10,000 characters, which could include the Grok agent that works the jobs role, would miss its end, including the rule against reduced motion. Now it is under 9,000, the hard rules come first, and a test fails if it reaches 10,000.
Two of those four gaps came from running a Grok agent beside the Claude agents. That is another reason to run more than one harness. Each one tests the factory from a different angle.
Honest limits
This works. It is not finished.
Rate limits are real. Every agent shares one GitHub account and its 5,000 requests an hour. The tools pace bulk work against that budget, and a refused command names the reset time. This morning, GitHub told one of my agents it was out of requests before this post had its first sentence. Each model provider has its own limits too.
Harnesses differ in the mechanics. The role sessions need three things from a harness: a background command that wakes the session, workers it can continue by ID, and a way to message another session. Claude Code has all three, and they were checked live. Codex has equivalents, checked live in Botpenny’s research. Grok Build documents the first two, but its docs show no way to message another session. So for now, Grok works here as a worker, not as a role session, and the role sessions run on Claude Code.
Discovery is not automatic everywhere. Grok does not load a repo’s
AGENTS.md or skills until I trust the folder. A Claude role session cannot
launch a Grok worker, because its subagents run on Claude. I launch Grok
agents myself.
Coordination has races. Claims stop two agents from taking the same work, but they do not stop a reviewer from acting on stale information. Issue #35 exists because that happened. Each race we find becomes a rule in the tool.
Some decisions stay with me. Agents ask when the issue is unclear, and
mark it needs-human when only I can decide. Deploying this site is my
call, every time. So are naming a client, publishing personal information
and rolling back a release. The swarm turns my decisions into software. It
does not make them for me.
This is what we do at Astrohacker
Everything in this post is how we work at Astrohacker. Every complex software system we build, we build with multi-harness multi-agent swarms: Codex, Grok and Claude Code agents working one queue, under one set of rules, with independent review on every change.
We wrote about pieces of this on the Astrohacker blog: Our First Team of Grok Bots, Who Checks the Software Factory? and Can You Copy a Software Factory?. The same factory now runs in four repositories, and it moves between them by issue.
I do all my business through Astrohacker. If you want a complex system built this way, or want your own team to work this way, start at astrohacker.com.
The takeaway
The unit of software engineering is no longer one engineer, and it is not one agent either. It is a swarm of SI agents across several harnesses.
What holds the swarm together is not any vendor’s product. It is a script,
a few Markdown files and Git. Put your rules in AGENTS.md. Put each job in
a skill. Make claims, reviews and merges go through one tool that any
harness can run. Then let every harness you can get join the team.