Tear it down before the bell
Three issues landed this stretch — the loadout, the cargo classes, and the run clock — and for the first time a run here has a beginning, an end, and a verdict. That’s real progress and it is not the whole sprint: the wasm size number that actually counts and the five-friend playtest that answers this project’s actual question are both still open. What follows is honest about which parts are which.
The loadout is a budget, not an inventory
You arrive with a fixed amount of mass to spend. Placing a block spends it. Removing a block gets it back — all of it, no restocking fee, no “returned items may be worth less.”
If you’ve ever watched a tabletop group discover that somebody has been ignoring encumbrance for six sessions, you already know the failure mode we were avoiding. Carry limits are one of those mechanics that is either load bearing or pure bookkeeping, with very little in between, and the difference is entirely whether the limit changes a decision you were about to make. A mass budget changes one: how much rig you commit before you know the shape of the problem.
That refund being whole was a real design choice, not the default you’d get by not thinking about it: parts coming back damaged, or partially refunded, or subject to some depreciation curve, were all explicitly on the table and explicitly rejected. The reasoning is that this is a puzzle whose whole pleasure is “I know exactly which corner is wrong — let me run it again” — and taxing the player for finding out is precisely the wrong lever to pull on a game about iteration. Every crafting game that’s ever charged you a restocking fee for breaking down your own furniture was, it turns out, teaching the wrong lesson.
There’s no gate yet, so tearing it down is the mechanic
The design calls for parts you “walk out with” to come back whole, which implies a gate you walk out through. This build doesn’t have one — no gatehouse, no threshold, nothing to cross. So recovery had to attach to something that already existed, and the thing that already existed was block removal. That turned out to be the right answer rather than a stopgap: whatever you haven’t torn down and packed away by the time the bell rings stays standing as litter, scored against you. The consequence is that dismantling your own machine before the clock runs out isn’t cleanup around the run — it’s part of it, on purpose, and forgetting to leave enough time for it is a loss condition you inflict on yourself and get to watch coming.
Two classes shipped. A third exists only as a sentence in the code
Everything you place defaults to run-rig — yours, recoverable, litter if abandoned. Cargo is the stuff actually flowing through your machine: it never persists, and whatever hasn’t reached the Drop when the bell tolls is simply gone, no salvage. Both of those are real and playable now.
There’s a third class, commons — infrastructure you deliberately donate, which would persist and never count against you, a pure gift to whoever runs the layout next. It does not exist in this build, because nothing persists across runs yet — there’s no layout for it to outlive. But it’s named, in the code, at the exact spot where run-rig and cargo are told apart, with a comment pointing at the design record that specifies it. That’s deliberate: leave the third class undocumented at the fork in the logic and someone eventually “helpfully” makes placement persist to fill the gap they assume was an oversight. It wasn’t. It’s just not this milestone’s job.
The clock doesn’t exist until it does
For most of a run, there’s no visible countdown at all — the HUD carries one
faint number in a corner (what you’ve delivered) and nothing else. The timer
only appears inside the last five minutes. We went in expecting that to be
two separate rules to implement — “show the clock under 5:00” and “show it
after the first bell strike” — and found out mid-build that they’re the same
rule wearing two descriptions: the first bell strike is the T-5:00 mark.
One if, not two. Occasionally the design documents agree with each other
by accident and it’s worth noticing when they do.
Heist films worked this out decades ago. Nobody watches the clock for the first hour of the job; the clock arrives when someone says it out loud, and from that moment it’s the only thing on screen. A countdown that’s been sitting in your peripheral vision for nineteen minutes isn’t tension, it’s furniture. We’d rather you spent most of a run reading the machine and only the last stretch reading the time.
The score is two numbers, and we turned down a third
At the bell you get: total mass delivered by the whole team, and how much of your own litter you left standing. Delivery is collective on purpose — nobody can honestly say whose belt carried which unit through a shared line, so the game doesn’t pretend to. Litter is individual, because it’s your rig and your call whether to tear it down.
We talked ourselves out of a per-player delivery number, which would have been the more satisfying screen — everyone loves a leaderboard. There’s no honest way to compute it. Any rule you’d write for “who delivered what” — whoever placed the sink, whoever placed the most blocks, some notion of contribution share — is a number the server would be making up, and this project doesn’t get to show a number it can’t stand behind. Two honest numbers beat three numbers where one of them is a guess wearing a stat.
The bug that would’ve waited two milestones to bite
Both litter totals — the team’s and each player’s — got computed by summing mass over a set of blocks. The team total summed every block standing in the world. The per-player total summed only the blocks that player placed. Right now those produce identical results, because every block anyone can place today belongs to whoever placed it. Nothing currently in the game can tell the two definitions apart.
The commons class above is exactly the thing that would have told them apart, the day it exists: a donated structure stays in the world (it still conducts freight, the analyser still counts it) but stops belonging to anyone. The team total, summed over the world, would have kept counting it. The per-player total, summed over placers, correctly wouldn’t. Two numbers on one screen, quietly disagreeing, and the disagreement would have meant the team score was punishing generosity — which the design forbids in as many words: generosity costs you materials, not your grade. One line fixed it — key both totals off the same ownership map instead of two different domains — and now a donated structure drops out of both by construction, whenever donation eventually ships. Catching a bug two milestones before it would have existed is an odd sentence to write and a good afternoon’s work.
Run two has to cost a rejoin, not a redeploy
The last piece is the least glamorous and arguably the most important: when the bell tolls, the world actually resets, and a fresh run starts the moment anyone joins again. That sounds like housekeeping. It isn’t. The entire point of this stretch of work is finding out whether anyone who plays a run wants a second one — and if getting a second run required restarting a server process by hand, we would have built a machine that can’t answer its own question.
The reset has one nasty trap in it: the mass budget lives on each connected player, not in one global place, so a reset that only clears the world and forgets the budgets starts run two with run one’s leftovers — everyone already half-spent before they’ve placed a single block. That fails silently. No crash, no red test, just a second run that quietly lies about what a fresh start looks like. There’s now one function that owns resetting everything in lockstep, and a test that runs an entire short bell end to end — join, build, delay, bell, rejoin, verify the world and every budget came back clean — so that particular way of lying to ourselves has a name and a red X waiting for it if it comes back.
What this isn’t yet
This is the shape of a run — a start, a budget, a clock that hides itself until it matters, an end, a verdict, and a fresh start after. It is not yet proof that the shape is fun. The wasm number that decides whether any of this reaches a browser honestly is still unmeasured against a tuned build, and the only test that actually matters — five friends, one server, a bell — hasn’t happened. Both are next.