DESIGN.md has opened with this since line 3 — "every callout is a different site, a different client" — and the game had the site and not the client. The forecast card is a job sheet now and the aftermath is an invoice. THE WEEK, once it was asked to name a client, mostly wrote itself: four nights are the same yard, so they're one client on retainer while they're away (the Hendersons, in Cairns from Friday — SPRINT11's own line). Night 3 is a different SITE, so it is necessarily a different CLIENT: a short-notice callout to a corner block you've never rigged, on the night the change comes early. That isn't flavour bolted on, it's what `site: site_02_corner_block` already meant, finally said out loud. BUDGET Y IS THE FEATURE, not the letterhead. DESIGN.md's brief is "client wants X, budget Y, forecast Z" and this card had Z alone. The fee has existed since Sprint 8 and the player has never seen it until the money was already spent — you chose what to rig without knowing what the job was worth, which is a reveal, not a decision. The sheet quotes base + garden + clean up front, and a test pins that a perfect night pays exactly what the sheet advertised: a job sheet that over-promises is the game lying on paper. THE CLEAN BONUS is the new money, and it's the one number in week.js that isn't measured. It gives the invoice three axes that mean three different things — you turned up, their garden lived, you didn't wreck the place — and it's NOT gated on winning, because a lost night where you broke nothing of theirs is a different fact and deserves its own row. It also makes the carport bite twice: 180 + 20 = 200, inside E's own "over ~250 is a silent game over" ceiling. Measured: it adds ~$100 to a competent week (385 -> 485), which makes BROKE_BELOW's known runaway worse. I did NOT pay for it by quietly trimming feeFor or gardenBonusMax — those carry measured evidence, and re-tuning someone else's evidence to fund my own feature is the move this repo keeps catching. If gate 3 says the bank runs away, this is the lever to cut first and it's data. TWO BUGS, BOTH CAUGHT BY LOOKING AT THE CARD, neither by a test: · "THE HENDERSONS" over "The Backyard — Your own place". The blurb and the letterhead called each other liars on screen. The blurb gave way: "your own backyard" is a leftover from when the game had one yard, and DESIGN.md never wanted it. The line's job survives — night 1 is the tutorial because you know this yard, now from having worked it for years rather than owning it. · C's `lead` is a 0..1 haze dial (confidence = 1 − lead), not "nights out". I read it as nights, passed 1, and the sheet advertised "forecast confidence 0%". Now 0.6, because C's own hail rule calls L > 0.55 too distant to promise ice, and tomorrow is exactly that. C: the slot is mine, the number is yours. Lane C's forecastLines(def, lead) finally has a caller above 0 — two sprints after they built it. Lane E: .letterhead / .brief / .jobsheet / .tomorrow are structure only and yours to style. selftest 304/0/0 (was 300). |
||
|---|---|---|
| .claude | ||
| docs | ||
| prototype | ||
| tools | ||
| web/world | ||
| .gitignore | ||
| DESIGN.md | ||
| LANE_PROMPTS.md | ||
| PLAN3D.md | ||
| README.md | ||
| server.py | ||
| SPRINT2.md | ||
| SPRINT3.md | ||
| SPRINT4.md | ||
| SPRINT5.md | ||
| SPRINT6.md | ||
| SPRINT7.md | ||
| SPRINT8.md | ||
| SPRINT9.md | ||
| SPRINT10.md | ||
| SPRINT11.md | ||
| THREADS.md | ||
SHADES — rig it, then survive the night
Geometry/tower-defense: you're a small person rigging shade sails against Australian storms. three.js r175 (vendored, no build step) + a zero-dep Python server. Everything the repo needs is in the repo.
Run it
python3 server.py --port 8801 # any free port
# game: http://localhost:8801/web/world/index.html
# selftest: http://localhost:8801/web/world/selftest.html (must be all green)
# 2D proto: http://localhost:8801/prototype/index.html (the original thesis)
No pip, no npm, no build. Python 3.8+ and a browser.
Read in this order
DESIGN.md— the design canon (what the game is)PLAN3D.md— the build canon (stack, contracts, lane model)THREADS.md— the append-only lane log. The project's memory. Read the last few[I]integrator entries to know the current state.SPRINT11.md— the current sprint (highest-numbered SPRINTn.md wins)LANE_PROMPTS.md— copy-paste prompts per lane, per sprint (bottom = newest)
The lane model (how this repo is built)
Five parallel Opus agents ("lanes"), each in its OWN clone, on its own branch:
| lane | owns |
|---|---|
| A | main.js, world.js, camera, hud, week, sites, server — shell & integration, merge shepherd |
| B | sail.js, rigging — cloth sim, economy, balance.test pen |
| C | weather, skyfx, debris, storm/site wind data |
| D | player, interact, ladder, broom — and the designated playtester |
| E | Blender asset factory (tools/blender), models/, textures |
Hard rules (they exist because each was earned — see THREADS):
- One clone per lane. Never two agents in one working tree.
- Rebase onto latest
mainbefore starting; small commits; push your branch. - File ownership is the merge strategy; cross-lane needs go in THREADS.md.
- Selftest green after every landing. Skips must be real. An assert that cannot fail is decoration.
- All sim code is deterministic
(dt, t)— no Date.now, no rAF. - Measure, don't reason: the repo's biggest bugs were all "a number gathered from the wrong harness". When two harnesses disagree, find the variable.
Setup on a new machine
Gitea's SSH runs over tailscale (the public host is Cloudflare-proxied, no
direct SSH). Add to ~/.ssh/config:
Host gitea.partly.party
HostName 100.71.119.27
Port 222
User git
Then:
git clone git@gitea.partly.party:monster/shades.git ~/Documents/shades
for L in A B C D E; do
git clone git@gitea.partly.party:monster/shades.git ~/Documents/shades-lane$L
git -C ~/Documents/shades-lane$L checkout -B lane/$(echo $L | tr 'A-Z' 'a-z') origin/lane/$(echo $L | tr 'A-Z' 'a-z')
done
~/Documents/shades is the integration checkout (main; where merges and
sprint docs happen). The five shades-lane* clones are where lane agents run.
Sprint cycle
- Fire lanes with the newest prompts at the bottom of
LANE_PROMPTS.md(Lane A usually first if the sprint gives it rulings/blocking items). - Lanes work, push their branches, log in THREADS.md.
- Integrate in the main checkout: merge
origin/lane/{e,c,b,d}(THREADS.md conflicts resolve keep-both-chronological; real code conflicts get read), run selftest, play the result, writeSPRINT(n+1).md+ prompts, push main AND fast-forward all fivelane/*branches to main.