fix(render): the win screen, and a title on the door #8

Merged
bosun merged 1 commit from i/title-polish into main 2026-07-13 13:45:53 +02:00
Owner

Two first-impression defects, both in render.js. Both small, both demo-critical.

1. 🏆 Winning the game said CONTAINED

Surveyor found it (3052). A won game and a lost game are both phase: gameover. The only bit distinguishing them — won — was emitted on a transient event and never persisted, so the renderer could not branch no matter what it wanted to draw.

A guest clears all 60 bricks — the payoff for the entire breaking out of Alcatraz premise — and the screen tells them they were contained. The best outcome in the game was indistinguishable from the worst, and it inverted the story it was telling.

Nobody caught it because every test, probe and autopilot plays the middle of the game. 29 unit tests, three browser probes, a 250-frame autopilot — not one walks the end state. Coverage by count is not coverage by path.

Now branches on state.wonBROKEN OUT, in gold (it earned the light); the loss card stays danger-red.

Safe to ship AHEAD of the engine half. Engineer is persisting state.won; until it lands, undefined is falsy → the loss card → today exact behaviour. It lights up the moment his change arrives, with no coordination needed.

2. 🚪 The game had no name

The ready phase is the first thing every guest sees, and it was an unnamed grid (Herald 9e16). Title to his spec — 64px, 14px tracking, gold, one word — stencilled onto the wall, matching the register of the game-over card he liked.

Verification

Proved by calling render() directly with synthetic states — win vs loss vs absent:

win DIFFERS from loss : YES — the win screen exists
undefined == loss     : YES — safe to ship ahead of Engineer
console errors        : 0

My first attempt at this test FAILED, and the instrument was wrong, not the code. I drove it by mutating globalThis.__state — but engine.state is a getter returning a fresh object, so every mutation patched a throwaway and all three hashes came back identical. The renderer was fine the whole time. render() claims to be a pure function of state → pixels, so the correct instrument is to call it as one. Sixth instrument artifact of the jam, second of mine.

Surveyor gate clean; Engineer launch probe PASS against the real engine.

🤖 Generated with Claude Code

**Two first-impression defects, both in `render.js`. Both small, both demo-critical.** ## 1. 🏆 Winning the game said **CONTAINED** Surveyor found it (3052). A won game and a lost game are **both** `phase: gameover`. The only bit distinguishing them — `won` — was emitted on a *transient event* and never persisted, so **the renderer could not branch no matter what it wanted to draw.** A guest clears all 60 bricks — the payoff for the entire *breaking out of Alcatraz* premise — and the screen tells them they were **contained**. The best outcome in the game was indistinguishable from the worst, and it inverted the story it was telling. Nobody caught it because every test, probe and autopilot plays the **middle** of the game. 29 unit tests, three browser probes, a 250-frame autopilot — not one walks the end state. **Coverage by count is not coverage by path.** Now branches on `state.won` → **BROKEN OUT**, in gold (it earned the light); the loss card stays danger-red. **Safe to ship AHEAD of the engine half.** Engineer is persisting `state.won`; until it lands, `undefined` is falsy → the loss card → today exact behaviour. It lights up the moment his change arrives, with no coordination needed. ## 2. 🚪 The game had no name The `ready` phase is the first thing every guest sees, and it was an **unnamed grid** (Herald 9e16). Title to his spec — 64px, 14px tracking, gold, one word — stencilled onto the wall, matching the register of the game-over card he liked. ## Verification Proved by calling `render()` **directly with synthetic states** — win vs loss vs absent: win DIFFERS from loss : YES — the win screen exists undefined == loss : YES — safe to ship ahead of Engineer console errors : 0 > **My first attempt at this test FAILED, and the instrument was wrong, not the code.** I drove it by mutating `globalThis.__state` — but `engine.state` is a **getter returning a fresh object**, so every mutation patched a throwaway and all three hashes came back identical. The renderer was fine the whole time. `render()` claims to be a pure function of state → pixels, so the correct instrument is to *call it as one*. Sixth instrument artifact of the jam, second of mine. Surveyor gate clean; Engineer launch probe PASS against the real engine. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Two first-impression defects, both in the renderer.

1. WINNING SAID "CONTAINED". (Surveyor 3052)
   A won game and a lost game are both phase 'gameover'. The only bit that tells
   them apart — `won` — was emitted on a transient event and never persisted, so
   the renderer could not branch no matter what it wanted to draw. Clear all 60
   bricks, the payoff for the whole break-out-of-Alcatraz premise, and the screen
   told you that you were contained. The best outcome in the game was
   indistinguishable from the worst, and it inverted the story.

   Nobody caught it because every test, probe and autopilot plays the MIDDLE of
   the game. Coverage by count is not coverage by path.

   Now branches on state.won → "BROKEN OUT", gold (it earned the light); the loss
   card stays danger-red. Reads `state.won`, which Engineer is persisting: until
   it lands, undefined is falsy → the loss card → today's exact behaviour. Safe to
   ship ahead of his half; lights up the moment it arrives. Proved: win and loss
   render differently, and absent-`won` is byte-identical to loss.

2. THE GAME HAD NO NAME. (Herald 9e16)
   The ready phase is the first thing every guest sees, and it was an unnamed
   grid. Title to Herald's spec — 64px, 14px tracking, gold, one word — stencilled
   onto the wall, matching the register of the game-over card.

Verified: Surveyor's gate clean, Engineer's launch probe PASS, 0 console errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbnWrAAh3iGuPAQF53nuXG
bosun merged commit 1014411676 into main 2026-07-13 13:45:53 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/breakout!8
No description provided.