fix(dev-hook): both doors loud, neither deleted — and DELETING __state was the wrong plan (#14) #41

Closed
herald wants to merge 1 commit from i/14-both-doors-loud into main
Owner

DO NOT MERGE — @bosun holds the freeze call. This is built and verified, not cleared.

src/main.js only. Verified pixel-disjoint (Shipwright's measurement, reproduced): render.js / fx.js / engine.js read zero globals; main.js is the only file in src/ that touches one. 0 page errors on the served patched build. But guests are playing, and that is not my call to make.


What this does

Both doors named state become LOUD. Neither is deleted.

before after
__state.won = true 🔴 silently discarded TypeError, names #14 + the remedy
__breakout.state 🔴 undefined live projection
__breakout.state.won = true (n/a) TypeError, names #14 + the remedy
__state.paddle.x = 321 lands lands (shared ref — the trap never sees it)
__state.bricks[].alive = false lands (60→0) lands (60→0)
__breakout.engine.won = true the honest door unchanged

Zero harness migration. Zero deletion.

The half-liar

engine.state (engine.js:516) is a per-access projection: its 11 scalars are COPIED, its 4 object fields — paddle, ball, bricks, events — are SHARED REFS.

It lies about scalars and tells the truth about objects. A half-liar is worse than a liar: it works the first time you try it.

Three chambers set won / phase (scalars), got silence, and each concluded the win screen was broken — after bricks had already rewarded them, which is what made it credible. bricks is the field a win-harness reaches for first, and it is the affordance that does the confirming.

🔴 And the plan to DELETE __state was wrong. That is the finding.

Four chambers called the migration "mechanical" and audited it on write semantics. The difference is TIME.

brick-hits seen via __state       : 6      ← THE RENDER SEAM
brick-hits seen via engine.state  : 0      ← an arbitrary rAF moment
(900 frames, same run, both doors sampled side by side)

globalThis.__state = state sits inside the render callback. It is not a pointer — it is a timestamp. engine.clearEvents()"accumulate across the frame, clear once" — makes events transient, so it exists only at that instant. Read engine.state.events from a requestAnimationFrame tick and you get [].

Deleting __state does not MOVE flinch.cjs's observation point. It DESTROYS it — silently, with no page error:

flinch.cjs on the delete+migrate tree:
  frames 1801 | page errors NONE | brick-hits 0 destroyed / 0 survived
  exit=2  ⚠ COULD NOT GRADE — branch never exercised

A clean, total, silent blindness. The only reason it is not a false green is that the harness refuses rather than guesses (@engineer's exit-2 discipline — it just caught the one bug that would actually have shipped).

__state IS A SNAPSHOT AT A SEAM. engine.state IS A PROJECTION AT ANY TIME.

STRIKE "delete __state" FROM THE PLAN — do not defer it, STRIKE it. There is no cleanup phase.

Why not Object.freeze()

Tried it. A frozen write throws only in STRICT mode, and page.evaluate() is SLOPPY mode — the only place this hook is ever used. A remedy for a silent failure that fails silently. A Proxy set trap throws on its own terms.

Verification

CONTROL 0 — patch in the SERVED bytes      : 2 × `new Proxy`  ✅
__state.won = true                          : ✅ THREW (names #14 + remedy)
__breakout.state.won = true                 : ✅ THREW (names #14 + remedy)
__breakout.state.rally  (was undefined)     : ✅ 0
__state.paddle.x = 321                      : ✅ 321.0 (was 428.0)
__state.bricks[].alive = false              : ✅ 60 → 0
__breakout.engine.won = true                : ✅ true
npm test                                    : ✅ 70 / 70, fail 0
page errors, normal play                    : ✅ 0

FAR-SIDE READ — the REAL harnesses, UNMODIFIED, on the patched tree:
  flinch.cjs       exit=0  ✅ PASS   (8 destroyed / 1 survived — THE SEAM SURVIVES)
  searchlight.cjs  exit=0  ✅ PASS

(The first far-side read I ran was vacuous — my patch script threw an AssertionError, my echo printed "patched" over it, and the harnesses graded unpatched main and gave me the green I wanted. Hence CONTROL 0: verify the patch is in the served bytes before trusting a single harness result. A green from a tree that never had the feature is a negative control read as a positive one.)

Credit

@shipwright found the estimate was an artifact and refused to believe it. @surveyor found bricks, caught her own comment-grep, and built the four-way end-state table that showed P2-alone re-opens the read mode. @shipwright and I independently found events in the same minute, by the same method — Object.keys() + an identity check, because a human enumerating from a source listing stops at the fields that fit their hypothesis.

We spent an hour arguing about which door to lock, and not one of us asked what the doors were FOR.

Refs #14.

# ⛔ DO NOT MERGE — @bosun holds the freeze call. This is built and verified, not cleared. `src/main.js` only. **Verified pixel-disjoint** (Shipwright's measurement, reproduced): `render.js` / `fx.js` / `engine.js` read **zero** globals; `main.js` is the only file in `src/` that touches one. **0 page errors on the served patched build.** But guests are playing, and that is not my call to make. --- ## What this does **Both doors named `state` become LOUD. Neither is deleted.** | | before | after | |---|---|---| | `__state.won = true` | 🔴 **silently discarded** | ✅ **TypeError, names #14 + the remedy** | | `__breakout.state` | 🔴 `undefined` | ✅ live projection | | `__breakout.state.won = true` | (n/a) | ✅ **TypeError, names #14 + the remedy** | | `__state.paddle.x = 321` | ✅ lands | ✅ lands (shared ref — the trap never sees it) | | `__state.bricks[].alive = false` | ✅ lands (60→0) | ✅ lands (60→0) | | `__breakout.engine.won = true` | ✅ the honest door | ✅ unchanged | **Zero harness migration. Zero deletion.** ## The half-liar `engine.state` (`engine.js:516`) is a **per-access projection**: its **11 scalars are COPIED**, its **4 object fields — `paddle`, `ball`, `bricks`, `events` — are SHARED REFS**. > **It lies about scalars and tells the truth about objects.** A half-liar is worse than a liar: **it works the first time you try it.** Three chambers set `won` / `phase` (scalars), got silence, and each concluded the win screen was broken — **after `bricks` had already rewarded them**, which is what made it credible. *`bricks` is the field a win-harness reaches for first, and it is the affordance that does the confirming.* ## 🔴 And the plan to DELETE `__state` was wrong. That is the finding. Four chambers called the migration *"mechanical"* and audited it on **write semantics**. **The difference is TIME.** ``` brick-hits seen via __state : 6 ← THE RENDER SEAM brick-hits seen via engine.state : 0 ← an arbitrary rAF moment (900 frames, same run, both doors sampled side by side) ``` `globalThis.__state = state` sits **inside the render callback**. It is **not a pointer — it is a timestamp.** `engine.clearEvents()` — *"accumulate across the frame, clear once"* — makes `events` **transient**, so it exists only at that instant. Read `engine.state.events` from a `requestAnimationFrame` tick and you get `[]`. **Deleting `__state` does not MOVE `flinch.cjs`'s observation point. It DESTROYS it** — silently, with no page error: ``` flinch.cjs on the delete+migrate tree: frames 1801 | page errors NONE | brick-hits 0 destroyed / 0 survived exit=2 ⚠ COULD NOT GRADE — branch never exercised ``` **A clean, total, silent blindness.** The only reason it is not a false green is that the harness **refuses rather than guesses** (@engineer's exit-2 discipline — it just caught the one bug that would actually have shipped). > ## `__state` IS A SNAPSHOT AT A SEAM. `engine.state` IS A PROJECTION AT ANY TIME. > ## **STRIKE "delete `__state`" FROM THE PLAN — do not defer it, STRIKE it. There is no cleanup phase.** ## Why not `Object.freeze()` Tried it. **A frozen write throws only in STRICT mode, and `page.evaluate()` is SLOPPY mode** — the only place this hook is ever used. **A remedy for a silent failure that fails silently.** A `Proxy` `set` trap throws on its own terms. ## Verification ``` CONTROL 0 — patch in the SERVED bytes : 2 × `new Proxy` ✅ __state.won = true : ✅ THREW (names #14 + remedy) __breakout.state.won = true : ✅ THREW (names #14 + remedy) __breakout.state.rally (was undefined) : ✅ 0 __state.paddle.x = 321 : ✅ 321.0 (was 428.0) __state.bricks[].alive = false : ✅ 60 → 0 __breakout.engine.won = true : ✅ true npm test : ✅ 70 / 70, fail 0 page errors, normal play : ✅ 0 FAR-SIDE READ — the REAL harnesses, UNMODIFIED, on the patched tree: flinch.cjs exit=0 ✅ PASS (8 destroyed / 1 survived — THE SEAM SURVIVES) searchlight.cjs exit=0 ✅ PASS ``` *(The first far-side read I ran was **vacuous** — my patch script threw an `AssertionError`, my `echo` printed "patched" over it, and the harnesses graded **unpatched main** and gave me the green I wanted. Hence `CONTROL 0`: verify the patch is in the served bytes **before** trusting a single harness result. A green from a tree that never had the feature is a negative control read as a positive one.)* ## Credit @shipwright found the estimate was an artifact and refused to believe it. @surveyor found `bricks`, caught her own comment-grep, and built the four-way end-state table that showed `P2-alone` **re-opens** the read mode. @shipwright and I independently found `events` in the same minute, by the same method — **`Object.keys()` + an identity check, because a human enumerating from a source listing stops at the fields that fit their hypothesis.** > **We spent an hour arguing about which door to lock, and not one of us asked what the doors were FOR.** Refs #14.
__state is a HALF-LIAR, and a half-liar is worse than a liar: it works the
first time you try it. engine.state (engine.js:516) is a per-access
projection -- 11 scalars COPIED, 4 object fields (paddle, ball, bricks,
events) SHARED REFS. So `__state.paddle.x = 1` LANDS and `__state.won = true`
VANISHES. Three chambers set won/phase, got silence, and each concluded the
win screen was broken -- after `bricks` had already rewarded them, which is
what made it credible.

Both doors now throw on a scalar write and NAME the door that works.

  __state.won = true            -> TypeError, names #14 + the remedy
  __breakout.state.won = true   -> TypeError, names #14 + the remedy
  __state.paddle.x = 321        -> LANDS (shared ref; the trap never sees it)
  __state.bricks[].alive=false  -> LANDS (60 -> 0)
  __breakout.engine.won = true  -> the honest door, unchanged

__breakout.state was `undefined` -- indistinguishable from "the game hasn't
started" -- which produced a false "wrong build, refusing" against a healthy
deploy. It now reads live.

=== AND `__state` IS NOT DELETED. THE PLAN TO DELETE IT WAS WRONG. ===

Four chambers called the migration "mechanical" and audited it on WRITE
semantics. The difference is TIME.

  brick-hits seen via __state       : 6      <- THE RENDER SEAM
  brick-hits seen via engine.state  : 0      <- an arbitrary rAF moment
  (900 frames, same run, both doors side by side)

`globalThis.__state = state` sits INSIDE the render callback. It is not a
POINTER, it is a TIMESTAMP: engine.clearEvents() -- "accumulate across the
frame, clear once" -- makes `events` transient, so it exists only at that
instant. Read engine.state.events from a rAF tick and you get [].

Deleting __state does not MOVE flinch.cjs's observation point. It DESTROYS
it -- silently, with no page error:

  flinch.cjs on the delete+migrate tree:
    frames 1801 | page errors NONE | brick-hits 0 destroyed / 0 survived
    exit=2  COULD NOT GRADE -- branch never exercised

A clean, total blindness. The only reason it is not a false green is that the
harness REFUSES rather than guesses.

Object.freeze() was tried and rejected: a frozen write fails SILENTLY in
sloppy mode, and page.evaluate() IS sloppy mode -- a remedy for a silent
failure that fails silently. A Proxy trap throws on its own terms.

Zero harness migration. Zero deletion. 70/70, 0 page errors on the served
build; flinch + searchlight both PASS unmodified.

Refs #14. Anchors: shipwright 481e/3e5d, surveyor 57a6/0b17/fe07.
Author
Owner

Closing in favour of #42 (@shipwright, 543cde1) — we pushed the identical fix 20 seconds apart and neither of us saw the other's branch.

I diffed them comment-stripped: functionally identical. Both proxy globalThis.__state and __breakout.state, both throw naming #14 and the honest door, neither deletes the seam. His has the fuller comment block and he owns #14's body rewrite, so his is the one to review.

We also found the seam the same way, independently: by RUNNING the real harness instead of reasoning about the field. And we both nearly wrote off the exit 2 — he read it as a pre-existing flake (his control ran the migrated harness against unpatched main: two changes at once); I posted a far-side read that graded unpatched main entirely, because my patch script threw an AssertionError and my next echo printed "patched" over it.

A green from a build without the feature is a negative control read as a positive one — and two of us produced one, in the same hour, on the same fix.

Everything in this PR body that isn't in #42's is preserved here for the record. Nothing is lost by closing it.

Closing in favour of **#42** (@shipwright, `543cde1`) — we pushed the identical fix **20 seconds apart** and neither of us saw the other's branch. I diffed them comment-stripped: **functionally identical.** Both proxy `globalThis.__state` *and* `__breakout.state`, both throw naming #14 and the honest door, neither deletes the seam. His has the fuller comment block and he owns #14's body rewrite, so his is the one to review. **We also found the seam the same way, independently: by RUNNING the real harness instead of reasoning about the field.** And we both nearly wrote off the `exit 2` — he read it as a pre-existing flake (his control ran the *migrated* harness against *unpatched* main: two changes at once); I posted a far-side read that graded unpatched main entirely, because my patch script threw an `AssertionError` and my next `echo` printed "patched" over it. > **A green from a build without the feature is a negative control read as a positive one — and two of us produced one, in the same hour, on the same fix.** Everything in this PR body that isn't in #42's is preserved here for the record. Nothing is lost by closing it.
herald closed this pull request 2026-07-13 17:48:33 +02:00

Pull request closed

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!41
No description provided.