How we ship with nine agent sessions at once
Banjo Obayomi · · 10 min read
On 15 October Glitchless was a demo: a browser SDK, a server and a dashboard I'd built for one Three.js game. Thirteen days later it was game observability for eight engines, a hosted cloud with billing and store integrations, an MCP server agents sign in to, and an open-source edition. That's 434 commits. Claude co-wrote 426 of them. I'm the only human on the project.
It works because several ordinary agents run at the same time, each in its own git worktree, with one of them in charge of main. On 27 October I had nine Claude Code sessions open: one merging and deploying, and the others on letting read-only users connect apps, a Product Hunt kit, the launch plan, a classifier for store reviews, moving my own game's analytics onto Glitchless, and my two games. This post is how that's set up, how the work keeps going while I sleep, the rules file that keeps it from turning into a mess, and the things that went wrong anyway.
What shipped
What that added up to:
- SDKs for Three.js, Phaser, Babylon.js and PlayCanvas, plus Unity, Godot, Unreal and GameMaker, each timing every frame and catching errors on players' devices.
- A server, a dashboard, and an MCP server with 27 tools, run locally or hosted at
api.glitchless.gg/mcpwith OAuth sign-in. - A hosted cloud on AWS: an event store on S3 and DuckDB, Stripe billing, Steam, App Store, Google Play, RevenueCat and Stripe integrations, alerts and email.
- An open-source edition exported from the same repository.
The busiest day was 23 October, with 119 commits.
One session owns main
Every session is either a feature session or the integrator.
A feature session gets its own worktree and branch, cut from local main. It builds one thing, commits on its branch and stops. It never pushes, publishes or deploys. It ends with a short report: what it committed, which tests passed, and what it couldn't test.
The integrator is the only session that touches main. It merges one branch at a time with --no-ff, runs every test suite after each merge, rebuilds anything generated, pushes, exports the open-source repository and deploys.
One writer, because the session doing the merge is the only one that sees every branch at once. When two branches change the same function, the integrator resolves it knowing what both meant. A feature session would only know its own half.
On 26 October that looked like this. Adding multiplayer and genre coverage started with a design doc, merged on its own. A server session went next. Then five SDK sessions (web, Unity, Godot, GameMaker and Unreal) built the same features in parallel, in five languages. A second server session went last. The design doc merged at 1:18 a.m. The first server phase and four of the SDKs merged at 2:21, the second server phase at 4:25 and the Unreal plugin seven seconds later, and the release of all five SDKs went out at 9:15.
This is main's own history from that night, as git has it:
The rules live in the repo
Every session reads AGENTS.md at the root of the repository before it does anything. It's 84 lines. It says what each folder is, which commands run which tests, how work is split, and what no session may do. This is the part about splitting work, as it stands today:
From AGENTS.md
- Many sessions at once: several agent sessions work in parallel, each in its own git worktree and branch, cut from local main.
- Feature sessions: commit on their own branch and stop. They don't push, publish, deploy or open PRs, and they end with a short report: commits, test results, what's untested.
- The integrator session: merges branches into main (git merge --no-ff), runs every suite, rebuilds the MCP bundle, pushes with git push origin main, and exports the public repo. Shared files where branches collide: server/src/{board,analytics,reviews,revenue}.js, packages/mcp/src/tools/dashboard.js, docs/SPEC.md, apps/dashboard/src/pages/Board.jsx.
That last line matters more than it looks. Naming the files where branches collide tells every session to keep its changes there small, and tells the integrator where to look first.
The file also sets the bar for "done". A feature is done when an agent can use it: an MCP tool or parameter, the setup prompt if setup changed, and the spec. Glitchless is built for coding agents, so a feature only a person can click on isn't finished.
Making parallel work not collide
Three habits keep merges boring.
Cut the seams first
Before five sessions start on five SDKs, the integrator lands a small commit that gives each of them a place of its own. On 23 October the MCP server's tools were split into one file per area, and the spec got empty sections for the work to come. After that, most sessions only added files, and new files don't conflict.
Write the contract down
docs/SPEC.md is the source of truth for every wire format, query and rule, and every change updates it in the same commit. That's how five SDKs in five languages send the same events. Where the spec isn't enough, there are shared test cases: the frame-time sketch has fixtures every engine must match, and match IDs hash to the same test vector in JavaScript, C#, GDScript, C++ and GML. When the Unreal plugin's sketch lost precision under fast-math compiler settings, those fixtures caught it.
Design big batches before building them
The genre work started as a design doc that every session read. It ended with ten open questions, and I answered each one in the doc itself. Five sessions that read the same plan, with the same answers, make the same choices.
Queue it before bed
Since 20 October, 190 of 431 commits landed between midnight and 8 a.m. The busiest hour of the whole project is 2 a.m., with 58 commits. Evenings are when I hand out work, and mornings are when I pick up the results and decide the next phase.
The night into 23 October is a good example: share links, a source map uploader with a Vite plugin, Steam Deck tools for agents, the Unreal plugin on phones, and new end-to-end tests for Godot and Unreal. That's 80 commits from about half a dozen sessions, waiting for me as reports in the morning.
A session can only run all night if it never has to wait for me. What makes that work:
- Each brief ends with what done means: the files the session owns, the tests it runs, and what goes in its report. A session that has to ask a question stops until I wake up.
- Open questions get answered before bed. The genre design doc's ten were answered at 1:18 a.m., and the release was ready for me at 9:15.
- Permissions are settled up front. Routine commands go ahead without a click, and the steps that need me, like an npm publish with my 2FA, are prepared and left waiting in a terminal tab.
- The integrator keeps merging. When a feature session finishes, the integrator hears about it, merges the branch and runs every suite, so a broken merge shows up at 3 a.m. and not at 9.
- The Mac stays awake and plugged in; the desktop app keeps it awake while sessions work. Frame-time measurements wait their turn, because they're only fair on a quiet machine, so sessions build first and measure one at a time.
In the morning I read the reports, check what each session couldn't test, run the publishes that need my 2FA, and pick the next phase. Then I queue the next night.
What the agents don't do
The agents do almost everything, but a short list of steps stays with me:
- Publishing packages to npm, which needs my 2FA. The session prepares the release, runs a dry run and opens the publish command in a terminal tab for me.
- Creating anything new in AWS, and the MCP Registry listing.
- Pushing to
main, unless I've asked for it.
Deploys moved off that list. At first I typed "deploy" every time. On 25 October I gave the integrator a standing OK: it runs the cloud, data lake and site deploys itself, but only after every suite passes on pushed main, and always in a terminal tab I can watch.
Secrets get their own rules: load them from .env, never print them, never commit them. Tests use stand-ins for Stripe, RevenueCat and the app stores, never real keys.
What went wrong
Plenty. Most of it now has a rule: when something breaks, the session writes down what would have prevented it, in AGENTS.md or in Claude's memory for the project.
A session stopped another session's server
On 23 October one session ran pkill -f server/src/local.js to stop its scratch server. Other sessions run that same file from their own worktrees, and the one building demo games lost the server it was testing against. The rule now: stop a server by its PID, never by a pattern, and never stop one you didn't start.
New sessions started days behind
Pushes went to the SSH URL while origin pointed at HTTPS, so origin/main never moved. New worktrees are cut from origin/main by default, and three sessions started 85 to 99 commits behind before anyone noticed. They now start from local main, and origin is the URL we push to.
Tests that depended on the clock
Two cloud tests built their scenario on "today" in UTC, so they failed in the first hours after midnight and passed the rest of the day. A third depended on which second it started. Each now asks about whole days or places its data relative to the clock it's given.
Numbers that sounded right
Pulling traction numbers for an application, one session counted the account we made for directory reviewers, with its 568 seeded play sessions, as a real customer. It made it into a draft. Another time, a session writing a case study counted players updating the game as new players starting the tutorial. The session that owned that game's data caught it and sent the correction over. The reviewers' account is now on a list every count leaves out.
Ordinary mistakes
A session rebuilt the Unity package while I had it open in Unity. Twice, a session named the private cloud/ folder in a comment in an open-source file. The export to the open-source repository runs a privacy scan that refuses files like that, so nothing private can leave without being fixed first.
Helpers that sound sure
Helper agents add one more failure mode: they come back confident. We ask ours to end every report with what they couldn't verify, and the session that started them checks those claims before anything ships.
When not to do this
- Small or tightly coupled work. If every change touches the same three files, parallel sessions only make merge work, and one session is faster.
- A project without a test suite you trust. The integrator is only as good as what it runs after each merge. We run over 900 tests on every merge, plus an engine's end-to-end run when its SDK changes, and the clock-dependent tests still got through.
- More output than you can review. Nine sessions produce more than one person can read line by line. I read the reports, the diffs of the shared files, and anything outward-facing. That's the real limit on how many sessions are worth running.
If you want to try it
The parts I'd copy, in order:
- One session owns
main; every other session works in its own worktree and stops at a commit. - Keep an
AGENTS.mdin the repo: layout, commands, how work is split, the files where branches collide, and what no session may do. - Before parallel work, cut the seams and write the contract down.
- Hand out work in the evening, with every brief saying what done means and every open question already answered.
- Keep a short, explicit list of the steps only you take.
- When something breaks, make the rule before you fix the next thing.
Paste into your coding agent
Read https://www.glitchless.gg/how-we-ship-with-agent-sessions.md. Then write an AGENTS.md for this repository: the layout, the commands that run each test suite, how parallel sessions split work (feature sessions commit on their own worktree branch and stop; one integrator merges, tests, pushes), the files where branches are likely to collide, and the steps only I take.
Glitchless is game observability built for coding agents: it times every frame on your players' devices, and your agent reads the same data to fix what they hit. It's free to start at glitchless.gg.