First players, first bugs
What changed in two weeks of going from private repo to embedded demo: real issue reports, the last Phase 1 gameplay seams, and one system that got smaller instead of bigger.
Two weeks ago the game lived in a private repo on my laptop. Today it’s embedded at /demo/play, the source code is on GitHub, and the issues page has real bug reports from people who aren’t me. That shift — from building in isolation to building under observation — changed what I worked on this week.
Here’s what landed.
Phase 1 is now actually playable end-to-end
The simulation has supported invading the enemy hive since Phase 09.1: a fighting ant standing on the enemy’s entrance descends into their grid, hunts the nearest hostile, and resolving combat against their queen triggers victory. That’s the win condition for Phase 1. But none of it was reachable from the UI — clicking on an enemy entrance did nothing visible, the keybind to swap underground views was undiscoverable, and there was no visual cue distinguishing “enemy entrance” from “any other surface tile.”
PR #29 wired up the four UI seams. Left-click on an enemy entrance now sets your rally point there. The X-keybind got a clickable button labeled “Your Colony / Enemy Colony.” Underground clicks on the spectator view became no-ops (instead of silently dispatching dig-marks against your own grid at the matching coordinates). And enemy entrances got a single-pixel red perimeter ring so they read as “rally here to invade” without any tutorial text.
The simulation work was already done. The lesson was that “feature ships when sim supports it” is wrong; the feature ships when a player can find it without reading the source.
One system got smaller
Phase 10 was originally going to add a dig-priority slider to the existing forage/dig/fight ratio that players control. After playtest, the slider got cut entirely. The behavior ratio is now two fields — forage and fight — and digging is auto-assigned based on outstanding marked tiles. Less UI, less to explain, and the simulation does the bookkeeping that a human would have done badly anyway.
This is the second time I’ve removed a control from the player surface in this project. The pattern: if a system has a single right answer most of the time, exposing it as a knob just lets the player make it worse. Auto-assign isn’t a smarter system; it’s a refusal to let the player accidentally starve the dig queue while staring at a UI element.
The first round of real issues
The issues page collected reports faster than I expected. The interesting ones, in rough order:
- #16 — queen wanders into the corner of her chamber and stays there. Visually unsettling; functionally fine. Fix was a wander policy that keeps her inside the chamber bounds rather than against an arbitrary wall.
- #15 — chamber food storage was render-authoritative. Storage capacity was being computed by the rendering layer reading sprite occupancy, which is exactly the inversion the seven principles say to never do. Made the chamber the authoritative source.
- #27 — carriers oscillating between food piles when colony storage was full. A worker would pick up food, walk halfway home, drop it because storage was saturated, walk back, repeat. Fix: bail out at pickup time, not at delivery.
- #30 / #31 — invisible ceiling-row dig marks. Clicking on the grass strip at the top of the underground view dispatched a real dig command against the tile underneath, but the renderer kept painting grass on top — zero feedback that anything happened, and the orphan marks were dragging idle workers into infinite loops. Fixed in three layers: input rejection, sim-handler rejection, and AI-controller pre-filter. Defense in depth, because the AI was creating ceiling-row marks too.
A pattern in this list: most of the bugs were input-layer or rendering-layer bugs surfacing simulation-layer symptoms. The sim itself, the part the seven principles protect, has been mostly fine. The friction is in the seams — the places where player intent translates into commands, or where world state translates into pixels.
How the game gets to the page
The library bundle work was the biggest mechanical chunk. The game builds itself as a single ESM module with a mount() entry point that the website’s Astro page imports dynamically. Cache-busted hashed filenames + a manifest.json so the website’s deploy can reference the right URL. A ready promise so the loading state knows when to clear. Source maps. A .d.ts file generated directly from the source so the public API can’t drift from its declared types.
It now takes a single push to game main to redeploy the live demo. The game’s CI bumps the website’s submodule pointer, the website’s deploy picks that up, the bundle builds with a fresh hash, and CloudFront serves it. None of that is interesting in isolation. What’s interesting is that the friction-to-ship is now low enough that I’ll fix bugs in the morning instead of batching.
The dual-AI review pipeline went live
Every PR on the public repo now gets reviewed by Codex automatically (skipped on owner-authored PRs, since I’m reviewing my own work via Claude already). The bug-report template emphasizes the F9 debug log, which produces the input-log + seed needed to reproduce a deterministic replay. The branch-and-PR workflow is now the rule, not the convention.
If you find something broken in the demo, I’d rather hear about it than not. The issue tracker is the place — the bug-report template walks you through the F9 debug log, which gives me a reproducible replay instead of guesswork.
More soon.