Skip to content

Two state leaks and a level exploit - #29

Merged
pshenok merged 1 commit into
mainfrom
fix/leaks-and-exploit
Aug 17, 2026
Merged

pshenok merged 1 commit into
mainfrom
fix/leaks-and-exploit

Conversation

@pshenok

@pshenok pshenok commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Three more from the audit. 558 → 560 tests.

The tariff never armed on the Tutorial entry. STATE.tariff.cycleOn = true was written in exactly one place: window.startGame. window.startTutorialGame calls beginRun() — which resetState()s it back to false — and then only showCeremony(). So the entry point a first-time player takes silently played a flat-rate game, while everyone who pressed Power On got the day/night meter. The mechanic most worth meeting early was the one the beginner could not meet.

The inspect selection outlived a run. clearWorld() hid the panel and restarted building ids at b1, but selectedId is module-scope in handlers.js and nothing cleared it. The next run's b1 is a different building, so tickInspect re-opened the inspector on it a frame later with nobody having clicked anything.

single_point_of_cold was won by bulldozing the plant and buying an identical one. The failure is permanent by design — chiller_fail sets repairAt = Infinity — because the level's entire lesson is that a second failure mode beats a second identical unit. But demolish refunds half of $260, turning the level's $200 into $330: enough for a replacement, straight around the lesson. Banning the type closes it, and preBuilt places with { free: true }, so the level still hands you its own plant.

The repo's own copy rule caught my first attempt at that last one — a level that bans a building it names in the briefing owes the player a reason. Both locales now give one, and it is a true reason: a chiller is not a same-day purchase, and an identical second one would only rebuild the same single point.

All three are mutation-checked; reverting any one turns its own test red.

THE TARIFF NEVER ARMED ON THE TUTORIAL ENTRY. STATE.tariff.cycleOn was
written in exactly one place, window.startGame. window.startTutorialGame
runs beginRun() — which resetState()s it back to false — and then only
showCeremony(). So the entry point a FIRST-TIME player takes silently
played a flat-rate game while everyone who pressed Power On got the
day/night meter. The one mechanic most worth meeting early was the one
the beginner could not.

THE INSPECT SELECTION OUTLIVED A RUN. clearWorld() hid the panel and
restarted building ids at b1, but selectedId is module-scope in
handlers.js and nothing cleared it — so the next run's b1 is a different
building and tickInspect re-opened the inspector on it a frame later,
with nobody having clicked anything.

single_point_of_cold WAS WON BY BULLDOZING THE PLANT AND REBUYING IT.
The failure is permanent on purpose (chiller_fail sets repairAt =
Infinity) because the lesson is that a second failure mode beats a
second identical unit. But demolish refunds half of , turning the
level's  into  — enough for an identical replacement, straight
around the lesson. Banning the type closes it, and preBuilt places with
{ free: true } so the level still hands you its own plant.

The repo's own copy rule caught the first attempt at that last one: a
level that bans a building it names in the briefing owes the player a
reason. Both locales now give one.
@pshenok
pshenok merged commit 54bf46a into main Aug 17, 2026
1 check passed
@pshenok
pshenok deleted the fix/leaks-and-exploit branch August 17, 2026 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant