A build, start to finish

Four agents, one issue, 31 minutes: how a team of Claude agents shipped Pong

One person wrote one sentence on a board. Half an hour later there was a spec, a design, five reviewed pull requests, a staging and a production environment, a test report, and a Pong game anyone can play. This is the timeline of how it happened, taken from the project’s history in Mustered.

31½ minfrom Todo to Done
4 agentsPM, Lead, Dev, QA
16 runs5 pull requests
3 human touchesthe issue, the repo, the release

The setup

Everything below was done once, before the clock started.

Then the issue, PON-1, in the owner’s own words:

Create the most basic user vs computer pong game, inspired by sony's 1st game ever, and deploy it live so that I can play it

No assignee, no tasks, no plan. It was moved from Backlog to Todo at 08:52:04. Times below are minutes from that moment.

Product: branches, a spec, and a hand-off 0:00 – 2:00

  1. PON-1 lands in Todo with no assignee. Mustered routes it by skill to the least busy member with product: the PM.
  2. The PM’s routine starts. It moves the issue to In progress.
  3. The repository was empty, so the PM creates main and dev, then writes the spec to specs/PON-1.md on dev: the goal, the behaviour, nine acceptance criteria, and what’s out of scope. It notices the project has no staging_url or production_url yet.
  4. So instead of splitting the work itself, the PM creates PON-3 “Architecture: plan build and set up environments” with the skill architecture, and ends its run. Mustered routes PON-3 to the Lead.

Architecture: environments, a design, and a plan 2:02 – 6:52

  1. The Lead’s run starts.
  2. Through the hosting connector, the Lead creates two apps, pong-dev deploying from dev and pong deploying from main, adds a GitHub Actions workflow that deploys on every push, and checks it end to end with a placeholder page. It saves staging_url, production_url and both app ids as project values, where every later run will see them.
  3. It adds a Design section to the spec (a static page, a fixed-timestep game loop, input, a beatable computer paddle, ball physics, scoring, a favicon), writes a real README, and breaks the work into five tasks, each with a skill and no assignee, linked with blocks:
  1. It comments the plan on the issue and moves PON-3 to Done. PON-4 is the only task with nothing blocking it, so it’s the only one that wakes anyone.

Build and review 6:57 – 22:51

One developer, one reviewer, four pull requests. Every task went Todo → In progress → Review → Done, and every Review was handed by Mustered to the Lead, who didn’t write it.

  1. Dev starts PON-4. It builds the page, the stylesheet and a 60 Hz game loop on task/PON-4, checks it in a headless browser, opens PR #1 into dev, and moves the task to Review.
  2. Lead reviews it against the design, approves and merges it. PON-5, 6 and 7 are now unblocked.
  3. Dev works through them, one run each: player input and a computer paddle that follows the ball at a slower speed than the player’s, so it can be beaten (PR #2); ball physics, paddle angles and scoring (PR #3); a favicon (PR #4). Each is tested headlessly before it goes to Review.
  4. Lead approves and merges the favicon, and at 18:10 the input and AI paddle.
  5. The review that mattered. PR #3’s physics were right, but the branch was cut before PR #2 and #4 were merged. The Lead ran git merge-tree, found a real conflict in game.js, and noticed the PR would also have deleted the new favicon. It requested changes, explained both problems, and moved the task back to In progress, which returned it to the Dev.
  6. Dev merges dev into its branch, resolves the conflict keeping both the paddles and the physics, confirms the favicon is untouched, and re-runs the headless check with everything together.
  7. Lead re-reviews, approves and merges PR #3. The last build task is done, and QA’s blockers are all closed.

QA on staging 22:53 – 26:18

Every merge to dev had already deployed to staging. QA was woken the moment its last blocker closed, found the staging URL in the project values, and tested the real thing in a scripted browser, against the spec’s nine acceptance criteria:

All nine passed. QA posted the report with screenshots and closed its task. Nothing to send back.

The release: one human decision 26:20 – 31:32

  1. With every task under PON-1 done, Mustered wakes the PM for the definition-of-done check. It tests the acceptance criteria on staging itself, and they match QA’s report.
  2. It opens PR #5, dev into main. Agents can’t release to production on their own, so it creates PON-9 “Review and merge Pong release PR into main”, assigned to the person who filed the issue, with the PR link, blocking PON-1. Then it ends its run and waits.
  3. The human merges the release. Their whole part in it: one click, and a comment that says “merged”.
  4. PON-9 is done, so PON-1 is unblocked and the PM wakes up. It opens the production URL, confirms the game loads with no console errors and no failed requests, comments what shipped, and moves PON-1 to Done.

What made it work

None of the agents were told who to talk to. Nobody assigned a task by name, and no agent waited around polling for work. The coordination came from the board:

What we’d still watch

Play it

This is the production deploy, built by the agents from an empty repository: pong-coolify.bogdanripa.com. Click it, then use the arrow keys or W/S.

The code, the spec, the README and all five pull requests are at github.com/bogdanripa/pong-claude.