cd /news/ai-agents/five-parallel-ai-builders-one-contra… · home › topics › ai-agents › article
[ARTICLE · art-153971] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Five Parallel AI Builders, One Contract, 25 Review Fixes

A developer built Refmade, a desktop app that visualizes Claude Code and Codex sessions as an office with a team lead and subagents, by coordinating five parallel AI builder agents from a single markdown contract (office-v2/P0-CONTRACT.md) that assigned file ownership and froze 125 existing tests. The first running version, P0, served on 127.0.0.1 with 224 passing tests and no outbound requests, using a per-run key passed after the URL fragment and checked with timingSafeEqual to keep the local view private on shared machines. A review pass produced 25 fixes, which the developer says reveals more about parallel agent work than the test count.

by read7 min views1 publishedOct 20, 2026

On September 26 I had five builder agents work from one markdown contract that said which files were theirs. When P0, the first local version, was done, it ran at 127.0.0.1 with 224 passing tests and no requests leaving the machine. Before that, a review pass ended in 25 fixes, and I think that number says more about parallel agent work than the test count does.

This is part of a series about building Refmade, a desktop app that draws your Claude Code and Codex sessions as an office: one team per session, with the main agent as team lead and each subagent as a teammate. Refmade is an independent app, not affiliated with Anthropic or OpenAI. Claude Code and Codex agents wrote most of the code, at my direction. I planned the work, made the calls, and reviewed what came back. The UI is Korean right now; that changes later in this series.

The previous posts covered why the office reads local logs instead of trusting agents to report. This one is about the first version that actually ran, the "P0".

A build contract, in my usage, is a document that fixes the interfaces between parallel workers and says which worker may edit which file. Without one, five agents writing one app produce five slightly different ideas of what a "team" object looks like.

The contract lives in the repo as office-v2/P0-CONTRACT.md. The file is dated September 25 and is about 40 KB today, because sections were added during the build; one of them, for the workflow view, comes up below. The part that mattered most was a table of who owns what:

Builder Owns Edits existing files
A log classification, Claude log reader none
B Codex log reader none
C office model, collector none
D HTTP server, the open command the CLI entry file and its translation keys, only to add the command
E the browser page and its scene code none
Integrator nothing new everything, but only for defects found at integration

Every builder got its own tests next to its own code. The 125 existing tests were frozen. No new dependencies, Node 18 or later, ES modules only. The logs under ~/.claude and ~/.codex are read-only for the whole app.

The rule I cared about most was the shortest one: "Other people's files are read-only." To change an interface, a builder had to edit the contract first and tell the integrator, not patch someone else's file. It is the same rule a team of humans uses, and it needed to be written down for agents for the same reason.

The server binds to 127.0.0.1 only. That is the easy part. The less obvious part is that a loopback port is open to every other program and every other user account on the same computer, so "local" is not the same as "private".

Builder D's contract section answers that with a per-run key. refmade open, a developer command that was never published, generates a random key and opens the page at http://127.0.0.1:<port>/#k=<key>. The key sits after the #, which browsers never send over the network. The page reads it and attaches it to every API call. This is the check as it exists in cli/lib/office/server.mjs today:

const keyOk = (req, params) => {
  if (!key) return true;
  const given = req.headers['x-refmade-key'] ?? params.get('k');
  if (typeof given !== 'string' || given.length !== token.length) return false;
  return timingSafeEqual(Buffer.from(given), key);
};

The ?k= fallback exists because EventSource, the browser API behind the live stream, cannot set custom headers. A request whose Host header is not the server's own address gets a 421, which blocks DNS rebinding. Static files need no key. The API returns nothing without it.

This protects the office view from other local programs. It does not make the agents' own work safe, and I do not claim it does. It only keeps the view private on a shared machine.

After the five builders finished and the integrator joined their work, I ran a review with four different angles, then a second round that tried to refute each finding. Findings that survived the refutation round became fixes. The total was 25.

I only have the count for the 25, not a list of what each one was. The log-reading traps from that day, such as a fork's copied transcript showing every teammate twice and one-shot runs looking like a resting team, are the subject of the previous post. One limit is still on my known-limits list: the one-line "result" under a finished request is the first sentence of the agent's last answer, and agents sometimes write short internal codes there that then show up on screen.

Two things went wrong in the process, not in the code. The workflow's own critique and fix steps failed because I hit a weekly usage limit, so I checked those parts by hand. And a few timing tests in the executor and file-watcher suites wobbled under load, though they passed when run alone. They are on the same known-limits list.

I wanted the office to feel more like a game. I also asked to drop the phrase "my turn" for a request waiting on the user, because it read oddly. That became the brief for three skins on the same data contract. Only the screen code could change.

Top bars of variants A (cozy sim), B (management sim) and C (retro RPG). Labels in Korean: "agents working", "finished requests", "hand-offs", "waiting for answer", "waiting for orders". Counts come from my own machine's logs, taken minutes apart, with team names cropped out.

The brief also listed what to leave out: particle rain, rainbows, level-ups, rewards, token bragging, anything decorative that was not tied to a real event. Effects were allowed only when something actually happened, such as a "+1 done" floating over the lead when a request ended. The font is Galmuri, a pixel font under the OFL license, bundled with the app so nothing loads from a CDN.

I picked B, the management sim. The test count was 245 once it was in place, up from 224.

The skin brief fixed the status vocabulary at five states: working, waiting for orders, needs your answer, not started, run ended. The words have drifted since. Today's game theme says "people working", "finished requests" and "waiting for confirmation" in its counters, and puts plainer sentences on top.

Counters in today's game theme: "people working 9", "finished requests 0", "waiting for confirmation 1". Public demo data.

A status sentence in the app: "2 teams are working. 1 needs my answer." The task name is sample data.

One caution carried over from this version. A finished request card means the request ended. It does not mean the work succeeded. The app reads turn boundaries from logs, and a turn that ended badly still ends.

I had told the agents that workflow runs were not showing up properly on screen. They took it to mean that the structure and the roles were invisible, and answered with an assembly-line view (stages across, branches down) and nine role badges, added to the contract as a new section. Around 22:00 I told them the Korean word they had picked for the branches read strangely, and they removed that word from the code and the page.

I kept the view and the word had to go. Terminology on a screen is part of the product, and an agent will happily ship a word that only makes sense inside the contract.

When P0 was done: 224 tests passing (245 after I picked the skin), a first load of about 2 seconds, and no request to anything outside 127.0.0.1 in the browser checks. The numbers are from my notes of that day's test and render runs.

They do not show that anyone can use the thing. My notes record no usability test with a non-developer, and 245 green tests say nothing about that.

Next up is a friends feature that I shipped to production and decided to drop on the same day. The app link will be in the last post of the series.

If you split one codebase across parallel agents, who owns the shared file, and do you use an integrator the way I did or let the agents merge each other's work?

── more in #ai-agents 4 stories · sorted by recency
── more on @refmade 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/five-parallel-ai-bui…] indexed:0 read:7min 2026-10-20 · —