Skip to content

Blog

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 read codex/issue-32 as a Codex agent blocking work. Now branches are named issue/N, and the old name still works until the last one retires.
  • #48, the file Grok might not finish. Our AGENTS.md had 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.