Planning with beads: how Physical Soccer and Joystick Jammers get built

My loop for new projects is deliberately boring.
- Write the plan. A real document: what the thing is, who it is for, what “done” means, and how we will know.
- Turn it into beads. Every requirement becomes an issue in beads, using Jeffrey Emanuel’s beads_rust (
br), stored in the repo next to the code, with real dependencies between them. - Let the graph decide.
br readylists only the beads with no open blockers. That is the whole scheduler. - Run a herd. With herdr and ntm I keep a few agent panes open side by side, each claiming one ready bead, so the work fans out and the graph keeps it honest.
- Review, playtest, write the next beads. My job is steering and taste.
Two of my current projects show what that looks like, and they show quite different shapes. One of them has since been rebuilt from scratch, which gave me a rare second run of the same game with a different method.
Physical Soccer: plan first, then let it flow
Physical Soccer is a party football game where anything can be a controller: phones, pads, keyboards, half a touchscreen. Before a single bead existed, it had a plan: plan.md, now on its fifteenth revision. Each revision closed real gaps. Revision 3 decided who owns the room state. By revision 4 the plan had a journey matrix and dependency cuts, and it said, in so many words, that it was “ready to convert into small, testable tasks”. The revisions kept coming anyway: revision 7 pinned the physics clock (the original game runs at 0.85x wall-clock, so the port carries an explicit time scale).
Then it was converted: a coverage index (plan-beads-map.md) maps every requirement to a bead ID, and every bead carries its own contract (what to build, which checks prove it, which evidence a parent bead needs from its children).
Physical Soccer: beads created vs closed, hour by hour
Physical Soccer: 194 beads, 451 blocking links, 24 layers deep
Why did this one flow so well?
- The plan did the arguing. Fifteen revisions of a document are cheap. Fifteen revisions of code are not. By the time beads existed, the hard decisions (who owns state, how reconnection works, what the relay is allowed to see) were already made.
- Every bead had a contract. “Done” meant named checks passed and evidence was recorded, so an agent could close a bead without asking me what I meant.
- Parents integrate, children prove. Small child beads close with targeted checks; the parent bead re-runs the whole thing. That stops “it works on my slice” from leaking upwards.
- Owner beads are explicit. The things only I can do (feel the controls on a real phone, judge whether it is fun) are beads too, assigned to me and fed by a reviewable build. They block exactly what they should and nothing more.
Joystick Jammers 0.1: a polishing swarm
Joystick Jammers is a party racer and demolition derby: the TV is the arena, phones are the pads. It is open source, so you can read the code and every one of its beads yourself. It is in a different phase. The game works, and the job now is polish: player readability, controller latency and reconnect confidence, tutorial and onboarding, wheelies and stunt payoff. That kind of work is discovered by playing, not planned up front.
Joystick Jammers: beads created vs closed
Joystick Jammers: 207 beads, 313 blocking links, 10 layers deep
The coordination rules are written into the repo, so every agent reads them on start:
- Register with MCP Agent Mail under a name, check mail before claiming work and after every edit-and-test cycle.
- Reserve the files you are about to edit (specific paths, never the whole repo), and announce claims, blockers and completion in a thread named after the bead.
- Keep the swarm small: at most five ntm panes including mine, and reuse idle panes as worker, validator or release manager instead of adding agents.
- Use
br(beads in Rust), and ask the graph what matters withbv --robot-triageorbv --robot-next. - Close a bead only when the change is done, the relevant tests pass, and a completion note is posted.
- Do not sit idle waiting for consensus. If a ready bead is unclaimed and you can make progress, claim it, reserve, announce, start.
Joystick Jammers 0.2: the same game, planned the Physical Soccer way
Polishing only goes so far. Jammers 0.1 was a Python server and a browser game grown feature by feature, and the next things I wanted (anyone dropping in mid-round, cars that shed doors and wheels, tracks generated from Australian places, controllers on WebRTC) wanted a different foundation. So 0.2 is a from-scratch rebuild: a Rust server, a Rust/WASM simulation running in the host’s browser, Three.js on the TV and WebRTC to the phones. The 0.1 game stays playable while 0.2 is built beside it.
This time it went the Physical Soccer way. A set of owner rulings came first, then a master plan, then a Playtest 1 plan cut down to one car and the new stack, and only then beads: every bead carries its plan task ID, its acceptance checks and the evidence it owes (the definition of done spells it out), and a bead map ties every plan requirement to a bead. All of it lives on the public v0.2-revamp branch, beads included. The repo even states the order of authority: owner ruling, then master plan, then Playtest 1 plan, then beads, then code. A bead cannot overrule the plan; if it disagrees, the plan gets fixed first.
Joystick Jammers 0.2: beads created vs closed, hour by hour
Joystick Jammers 0.2: 193 beads, 616 blocking links, 17 layers deep
Two things changed from 0.1, and both came from Physical Soccer:
- The plan is the source, not the backlog. In 0.1 most beads came straight from playtests, so the backlog was the plan. In 0.2 beads are cut from the plan, so a bead that turns out wrong sends me back to the plan instead of spawning three more beads.
- Closing is verified by someone else. An agent that finishes a bead hands it to a verifier, which checks the bead’s evidence against a green CI run on a commit that contains the work. It costs a little latency and saves a lot of “it was done, honestly”.
Where I am the bottleneck
The layered drawings above answer “how deep is this”. They hide a question I care about more: how much of this is waiting on me? In both projects some beads can only be closed by me: a design or taste review, a couch playtest with real people and real phones, a release call. Agents can prepare everything around them, but they cannot close them. So I drew the same graphs the way a beads viewer does, as a force-directed network where tightly linked work clumps together, and picked out my own gates.
Joystick Jammers 0.2: my 6 gates and what waits behind them
53 of 193 beads sit downstream of at least one of my 6 gates; 4 of them are open and waiting on a gate I have not closed yet.
That is the honest cost of taste. Design reviews are cheap for me to do and expensive to wait for, so in 0.2 they became small, frequent rounds with their own beads (round 3, 4, 5) instead of one big verdict at the end, and the agents work on everything that does not depend on the look while a round is open.
Physical Soccer: my 16 gates and what waits behind them
36 of 194 beads sit downstream of at least one of my 16 gates; 12 of them are open and waiting on a gate I have not closed yet.
Two habits came out of looking at this:
- Put the human gates in the graph, explicitly. If a playtest is only in my head, agents will happily build past it and I find out late. As a bead, it blocks exactly what it should, and the graph shows me when I have become the critical path.
- Split big human gates into rounds. One “approve the style” gate at the end blocks everything after it. Several small review rounds, each blocking only the screens it judges, keep the herd busy while I catch up.
Deep vs wide
| Project | Beads | Blocking links | Longest chain | Closed | Shape |
|---|---|---|---|---|---|
| Physical Soccer | 194 | 451 | 24 | 64 | planned, deep |
| Joystick Jammers 0.1 | 207 | 313 | 10 | 114 | polishing, wide |
| Joystick Jammers 0.2 | 193 | 616 | 17 | 131 | planned rebuild, deep |
| This site (previous version) | 99 | 117 | 7 | 85 | small, quick |
The longest chain is the number I watch now. A herd of agents makes a graph wide cheaply: more panes, more parallel beads. Only planning makes it shallow, by finding the dependencies early and cutting them where they are not real. Physical Soccer’s 24-deep chain is the honest critical path of a networked physics game with a relay, two hosts and owner playtests in the loop; Jammers 0.1’s 10 layers is what let a five-pane swarm close two dozen beads a day. Jammers 0.2 sits in between at 17: the same game, but a rebuild has to lay its foundations in order, and the plan made that order explicit before the first agent started.
The part that surprised me is how much the graph helps me. When something goes wrong, the fix is usually a better bead (clearer acceptance checks, a missing dependency), not a better prompt. The wording I hand an agent barely matters any more; a clear goal, a real way to check the result and the effort to keep going until it passes matter a lot. I made the first half of that argument in Your Prompts Do Not Matter (Anymore): prompt engineering stopped being the hard part. Beads are where the rest of it lives.