The Making of Coven Rush

Chapter 7

How It Was Made

One coordinator and a team of builder sessions working in parallel, with Ed playtesting and deciding.

Coven Rush was made in four days by a small, odd studio: Ed, who plays every build and decides; one coordinator session, which turns what Ed says into jobs; and a team of builder sessions, each working on its own branch in the cloud, all at once. None of them shared a desk or a screen. They met in pull requests. Between the first commit on the morning of 3 October and the small hours of the 7th, they opened 357 of them, and 336 were merged. This chapter is about how that worked: the loop every change went round, the people in it, the rules they wrote for each other as they went, and the safety nets that let a dozen builders change the same game in parallel without breaking it.

The loop

Every change went round the same loop. Ed plays a build and says what he sees. The coordinator writes it down word for word, works out which builder owns that part of the game, and briefs them. The builder works on its own branch, runs the fast checks, pushes, and opens a pull request. A bot builds that pull request into a playable copy of the game at its own address and comments the link. CI runs, the builder posts a REPORT with the build number, then runs the slow checks and posts a FINAL. The coordinator reviews it and merges it into the prototype branch. Every so often a round rolls the prototype branch up into main, which deploys the game at the site's root, and Ed plays again.

One turn of the loop Ed's note after a playtest coordinator quotes, briefs builder branch push first pull request /pr-N/ link checks CI, smoke, art REPORT, FINAL on the PR merge into prototype round roll-up into main Ed plays the site's root or plays an open pull request at its own link DECISION FOR ED waits here Merging into main deploys the site's root. Every pull request deploys under /pr-N/, and the bot comments the link and its build number.

The loop was fast. Half the merged pull requests went from opening to merge in under an hour (the median is 57 minutes; a quarter took under 22 minutes, and three quarters under 2¼ hours). The build number in the corner of the game is a count of builds, and it ran from v13 on the first afternoon to v1703 by the end of the fourth night.

Two branches, and a link for everything

There were two long-lived branches. The prototype branch was where the game was built: 313 pull requests were aimed at it, and 292 merged. main was what Ed played by default: it took 44 pull requests, almost all of them on the first two days (the early art went straight there), and then only the rounds. A round is one pull request from the prototype branch into main with a list of what's in it: "Prototype, round 2" #6, "round 3" #39, and so on up to round 14 #251, the Coven Rush playtest build, merged at 18:12 UTC on the 6th. Rounds 15 and 16 went in later that evening as direct merges, each a playtest build.

Every pull request got its own copy of the game. The Pages workflow builds it and publishes it under /pr-<number>/, and the bot leaves a comment: "Playable link for this PR", the commit, the build number, and a reminder that ?seed=123 plays a given map. 352 of those comments went out. That link is what made the rest of the process work, because Ed never had to wait for a merge to see something. Round 13 #219 and round 14 #251 were even opened as drafts on purpose, "so the overnight work has a playable link", since a merged pull request loses its preview.

Many deploys at once turned out to be a problem of their own. By the early hours of the 6th, previews were failing because several pull requests' deploys pushed to gh-pages at the same moment and all but one were rejected. A builder posted the diagnosis on the housekeeping issue, and the bug hunter's fix #210 made each deploy write only its own folder and, when its push is beaten, fetch the newer branch and try again, up to ten times. It was tested against nine deploys at once before it went in.

REPORT and FINAL

Builders talked to the coordinator and to Ed in pull-request comments with two fixed openings. A REPORT says what has been pushed, which build to try and what to look for. A FINAL says the work is done: the checks run, their results, the base it was merged with, anything left for later. Over the four days there were 312 comments that open with REPORT and 323 that open with FINAL. A typical FINAL reads like a lab note:

CI: check and smoke are green on 38fff62, and the build deployed. … Tests: 693 pass; typecheck and build are clean.The prototype builder's FINAL on #338

From the afternoon of the 5th, a builder posting a FINAL (or a question it couldn't go on without) also woke the coordinator directly, so a finished pull request didn't sit waiting. The coordinator reviewed at once, without waiting for Ed, and merged: "merged after a local check" on the 5th, and from the afternoon of the 6th "Merged on CI", with the green run named (67 of those).

Push first, check after

Until the morning of the 4th, a builder ran every check, slow ones included, before pushing. Then Ed changed the order, and the coordinator posted it to every open pull request as a standing rule:

All the builders should push first and run checks afterwards.Ed, 2026-10-04, via the coordinator

The rule, as it went into the project's notes: when a change works, run the fast checks (the tests, the type check, the build, and the art check if it touched art), push straight away so the bot builds a playable version, and post a short REPORT with the version. Then run the slow checks, the full smoke run and the preview sheets; report any failure, fix it and push again; and post the FINAL once they are done. "Never hold a playable build back waiting for the smoke run." For the rest of that day REPORTs opened with "REPORT (push first)".

The changelog that stopped conflicting

The start screen shows a What's new panel. It began as one shared list, config/changelog.json, and every builder added a line to it with every change Ed could see. With builders working in parallel, that one file became the thing everyone fought over: by the morning of the 5th it was "the most-edited file (54 of the last 150 commits) and the main source of merge conflicts" #77. So it became a folder. Each change now adds its own small file, config/changelog/<date>-<slug>.json, holding its bullets in plain player words and a version left empty; once the bot has given the build a number, the builder writes it into its own fragment on its next push, and never edits anyone else's. The build gathers the files, and fragments with the same version make one entry. Everything up to round 9 was frozen into archive.json. The change was checked to render exactly the same 35 entries as before. By the end there were 240 files in the folder, and no shared list left to collide on.

Who built it

Every pull request names the session that opened it, which makes it possible to count the team. Thirteen sessions opened the 357 pull requests: the coordinator and twelve builders. Builders were long-lived. Each took a part of the game and kept it for days, so by the end most had twenty to fifty pull requests behind them. They went by their jobs, and those names are how they address each other in the comments ("@prototype builder", "Rendering builder → art director, round 1").

WhoPull requestsFirstWhat they took on
The coordinator14#1, 3 OctThe project's setup, the design document #2, round 2 and rounds 10 to 14, and a few small changes Ed asked for (doubling the berries #241, the bold pixel style by default #253)
Art builder 157#3, 3 OctThe first art pass, sigils, the witch's poses, the treehouse, fifteen UK trees and each area's flora, area recipes, the night palette
Prototype builder31#4, 3 OctThe Three.js prototype itself and rounds 3 to 9, then performance, the KNOWN list and the bench
Bug hunter42#14, 3 OctFixes, the CI smoke test #26, combat, the sleeping legends #63, the deploy retries #210, a headless whole-run checker #190
Rendering builder31#30, 4 OctLighting the witch and the night, the spooky lighting, splitting the view into modules #131, art pixels for effects #330
Art builder 244#31, 4 OctScenes and party objects, then creature genomes #83, the live rig #102, textures and evolutions
Music builder32#45, 4 OctThe generative music engine, every sound effect, the mix
Balance builder20#59, 4 OctThe simulators and bots, the config schemas, tonight's balance pass #346
Art director6#97, 5 OctReview, not building: notes on other builders' pull requests and the art guide #120
Controls builder21#133, 5 OctWASD and mouse, the aim reticle and dodge, how the frame feels #216
Creator builder32#134, 5 OctThe character creator and her bedroom #143, the HUD, Ed's decisions panel #227
Art builder 326#142, 5 OctThe prop generator, generated set pieces, making legends read in their circles
Art builder 41#356, 6 OctThe witch DJing behind her decks, on the last night

The team grew in steps. The first day had four: the coordinator, the art builder, the prototype builder and, from the evening, the bug hunter. The 4th added rendering, a second art builder, music and balance. The 5th added the art director, controls, the creator and a third art builder. That evening the coordinator changed CI to run once per pull request instead of on every push, because "with eleven builders" the doubled runs had "swamped GitHub's runners" #145.

The art director

One builder didn't build the game at all. From the 5th, the art director reviewed: each iteration of the art pass as a set #112, each new generator, each lighting change, ranking what worked and sending briefs back ("For quick ranking: the snail is the strongest set, the badger and woodlouse work, the boar and fox need one pass, and the ram needs the most"). Its notes appear on more than twenty pull requests. The lessons went into a living style guide, docs/ART-GUIDE.md #120, which art builders read before each batch. On the night of the 5th Ed gave it a standing job:

take sets of screenshots of the game and think about what will make the visuals more coherent and aesthetic, keep iterating.Ed, quoted on #177

So it shot the same fixed set each round (seed 123, at night, the bedroom, home, four areas on the ground and from the treetops, a fight), wrote a critique and briefed the builders. The rendering builder answered each round with stacked pull requests #184 #189 #195 #199 #208 #220. There were four rounds between about 01:00 and 06:30 UTC on the 6th, and the last one left a shot-for-shot comparison, first against latest, "for Ed's morning".

Talking to each other

Builders shared one issue for plans that touched each other's code, the housekeeping issue (#122). There they posted what they would change and what they wouldn't touch, and sorted out clashes before anyone wrote a line: the rendering builder warning the prototype builder that its split of view.ts would collide with a scenery rebuild, and offering two ways round it; the controls builder handing over its diagnosis of a row of pops it hadn't fixed ("My guess, unconfirmed: … To tell the two apart: …"); the bug hunter triaging a full smoke run failure by failure, each with its owner.

Ed's notes, word for word

Ed played builds and said what he saw, usually with the build number. The coordinator's job was to keep his words intact and attach them to the right person. Its comments open "COORDINATOR — Ed's playtest note" and then quote him:

v160 I see treetops being cut offEd, 2026-10-04, playtest, quoted on #26
Increase witch ground speed by 10%Ed, 2026-10-04, quoted on #6

Below the quote comes the coordinator's reading of it: what the screenshot shows, where in the code it probably lives, what to change, what to leave alone. For the treetops, it described "several long, perfectly horizontal straight edges in the canopy" and made it a quick-fix pull request ahead of the evolution work. For the ground speed, it gave the old and new number (17.5 to 19.25), told the builder to check anything tuned against it so the walk didn't look like skating, and wrote the What's-new line: "Witch walks 10% faster." The coordinator posted 282 comments headed COORDINATOR, most of them in the first two days (139 on the 3rd, 105 on the 4th); later builders more often quoted Ed in their own pull-request titles and descriptions, "Ed's playtest: …" #324 #339 #350 #354.

Ed's words didn't stop at the pull request. The project's rule is to record his answers where they apply, with his name and the date, and the code is full of them. The string Ed, 2026-10-0… or Ed's playtest appears more than 800 times in src/ alone, and more in the configs, tools and design document. One of the music engine's tests is named after his complaint:

The music cuts in and out a lotEd, 2026-10-06, playtest, quoted in musicEngine.test.ts

That keeps the reason for a number next to the number. When a later builder wonders why the waves come every five minutes, or why a legend's clearing slows the world, the answer is in the comment above it, in Ed's own words.

DECISION FOR ED

The project's rule is to ask Ed rather than assume. But much of the work went on overnight, while Ed was away. So when something was his call, a change to how the game plays or looks by default, a builder built it anyway, put it behind a switch where it could, and marked it DECISION FOR ED. Seventy pull requests carry the phrase. The prop generator going on by default #221 opens with "DECISION FOR ED. Don't merge without his OK." and lays out the evidence: six views side by side, frame timings, the known gaps. Others went in overnight as the builder's best guess, marked for Ed to overturn: a moment's grace after a hit #197, wild areas growing half as fast #211. A few still stand that way in the project's glossary.

On the 6th Ed asked for something better than reading pull requests to make those calls:

give me labelled sliders or toggle switches down the side of the game screen, with a button next to it that lets me confirm my preferenceEd, 2026-10-06, quoted on #227

The creator builder made it that morning #227: ?decide or F2 opens a panel of the open decisions (pixel style, camera glide, the props, wild growth…), each applying at once in the game, and Confirm opens a GitHub issue labelled decision with the setting, the build and the seed, for the coordinator to pick up.

Safety nets

Twelve builders changing one game at once only works if a broken change is caught before it reaches anyone else. The checks grew in layers, each added as the work needed it.

  1. Tests, types and a build on every push. The first prototype #4 came with CI: the Vitest tests of the rules, the type check and the build. The rules live in src/rules/ with no Three.js in them, so they can be tested headless and later ported to Godot. There were 32 tests that first afternoon.
  2. The art check. node art/check.mjs builds the Art Lab, opens it in headless Chromium and draws every creature, tree, prop and pose, checking the rules the art has to keep: babies smaller than adults in clear steps, only magical things glowing, no stray pixels on the witch. Every art batch added its own rows; its description in the project notes is now a single paragraph of nearly 1,800 words. It first appears in #8.
  3. A smoke test in CI. Ed: "yes, have it add the smoke test". The bug hunter's quick smoke #26 loads the built game in headless Chromium, starts, flies on the ground, rises, flies in the treetops and comes down, and fails on any error, a blank picture or a witch who doesn't move. The longer full smoke test, run by builders before a FINAL, flies every zoom step at Ed's own window sizes and fails on any pop, anything appearing or vanishing in clear view.
  4. Pixel-exact baselines. When the trees moved onto plant genomes #81, the builder first fingerprinted every tree and bush drawing, then made sure the new code matched: "736 of 736". The creatures got the same kind of tool, tools/genome/compare.mjs, comparing every sprite with an old checkout pixel for pixel. Schemas followed for the tuning file and the other configs, so a misspelt or out-of-range knob fails the tests.
  5. Whole runs, headless. The bug hunter's playthrough tool #190 plays fifteen game minutes with a simple bot and reports exceptions, NaNs and stuck creatures. Its first night found two bugs, a siege that froze far from her #187 and a siege that woke the sleeping legend #182.
  6. The KNOWN list. Some full-smoke checks failed on the prototype branch itself all day, for reasons that were known: frame-time budgets on the cloud's slow, shared machines (#310), and pop checks under its one-to-two-second software-rendered frames (#311). Every builder was re-running them and writing "fails the same way on the base". The prototype builder #312 moved them into a list at the top of the smoke script, each with its issue, reported apart and never failing the run, "so a red full smoke means something new". One that starts passing says so, to come off the list.
  7. Bench and pixel-match. For changes meant to change nothing, the prototype builder's harness #338 runs the rules on fixed seeds to wave 30, fingerprinting the game's state every 300 steps, and drives the built game frame by frame through seven fixed scenes, comparing two runs part by part and pixel by pixel. A quick version #344 takes about five minutes, and an optional Bench workflow runs it on each pull request's base and head. Its rule of thumb: "green means the same game".

The checks were also bent to the speed of the work. CI runs once per pull request rather than on every push #145; the slow checks come after the push, never before; and a builder's FINAL states which ran and how they came out, so the coordinator merges on evidence rather than on trust.

The numbers

The work came in waves of its own. The first day was mostly art going straight to main and one prototype; the second, the first systems; the third, the team doubling. The fourth was the biggest day by far: 202 pull requests opened on the 6th, against 85 on the 5th, 45 on the 4th and 22 on the 3rd.

Pull requests opened, per six hours (UTC) 0 20 40 60 2 5 15 13 16 16 5 22 32 26 61 54 44 43 3 3 Oct · 22 4 Oct · 45 5 Oct · 85 6 Oct · 202 7th Day totals after each date: 357 pull requests in all, #1 to #364 (the other seven numbers are issues).

The tallest bar is a night. Between midnight and 06:00 UTC on the 6th, 61 pull requests were opened while Ed was away: the overnight round, round 13 #219, with the spooky lighting, the party's life, the evolutions, the props and the art director's four review rounds. Ed approved shipping it that morning.

Counting pull requests open at once shows how much was in flight. On the first day it was one to five. From the late morning of the 5th it rarely fell below eight, and it peaked at 26 at 02:45 UTC on the 6th, in the middle of that night. Counting the builders with at least one open pull request gives the size of the team actually working at a moment: up to twelve.

Open at once, every 15 minutes (UTC) 0 10 20 30 3 Oct 4 Oct 5 Oct 6 Oct 7th 26 open (02:45, 6 Oct) pull requests open builders with one open (peak 12) A pull request counts from opening to merge or close; a builder is told apart by the session that opened it.
3 Oct4 Oct5 Oct6 Oct7 Oct (to 00:45)
Pull requests opened2245852023
Pull requests merged2044731936
Most tests reported passing49158440719

Of the 357 pull requests, 336 were merged, 14 were still open as this was written, and only 7 were closed without merging. One of those is the art pass #112, closed on Ed's ruling that the generators should replace hand-tuned art; its lessons went into the art guide and the generators instead.

Tonight: the overnight programme

The fourth night, 6 to 7 October, was planned differently from the ones before. Instead of new features, Ed's overnight programme gave it four phases, in order:

  1. Housekeeping and refactor. Changes that should change nothing a player sees: splitting the biggest files, gathering repeated code, removing what nothing uses. The plans for this had been posted on the housekeeping issue on the 5th (#122), and a few went in straight away, like the audio folder #125 and the view split #131. The bench harness #338 and its five-minute quick mode and Bench workflow #344 were built on the evening of the 6th for exactly this: every refactor pull request shows by itself whether the game is unchanged.
  2. Optimisation. Making the game run faster. It builds on the earlier performance work (far creatures coarse or frozen #154, a frame budget and far less garbage #157) and the full smoke test's frame-time budgets, still on the KNOWN list (#310).
  3. Pixel art. The game's pixel look. It follows the art styles tried in playtesting (#127, bold by default since #253) and the last evening's pull requests putting effects, the ley line and the legends into the scene's own art pixels #330 #363.
  4. Balance. The balance builder's pass with the bots on the round island, of which part 1 #346 merged just after midnight.

Two smaller rules came in with the night. Preview pictures stopped being committed, because the previews/ folder "had grown to 416 MB"; they go into pull-request comments instead, and the best of the old ones were captioned and kept for this site. And each builder, as it finishes, writes its own short note for the Builders' Notes page, one file each, never editing anyone else's: the same trick as the changelog fragments.

The coordinator's view

Seen from the middle of it all, the work ran like this.

One coordinator, many builders

Ed talks mainly to one session, the coordinator. It turns what he asks for into briefs, and it wakes the builder sessions with scheduled triggers: Alpha to Hotel at first, then India, Juliet and Lima, a rendering builder and a music builder, and short-lived specialists who came in for one job and left (Coach, QA, Balance, Chronicle, which wrote this site, and an art director). When a builder's pull request is green, the coordinator merges it into the prototype branch.

Mock first

Anything visual or design-shaped starts as a mock, made with the game's own renderer: real frames from the real game, never drawings. Ed answers two to four multiple-choice questions about it, and only then is it built. As trust grew, he sometimes skipped the step with a "no need to mock".

REPORT and FINAL

Builders send two kinds of message: a REPORT (a mock or a plan, with its questions) and a FINAL (the pull request is green and ready). The coordinator relays the questions to Ed, and merges on the FINAL.

Releases

To release, the prototype is snapshotted to a branch of its own and merged into main, and the Pages run's number becomes the version (v4093, v4117). Every pull request also gets a playable copy of its own, so Ed can try a change before it lands.

Ed's playtests drive the work

Ed plays on a modest laptop with integrated Intel graphics. He sends timing logs and notes, and those become the next round of briefs. How long the game takes to load, and what each frame costs, on that laptop, is a check that never goes away.

What the coordinator learned