THREADS: Sprint 13 gate 3 + 2.5 landed; M-mute ask to C; gate-1 rulings parked on C's diagnosis

This commit is contained in:
type-two 2026-07-18 01:22:31 +10:00
parent 701403ab1d
commit 6cf5b07f32

View File

@ -4561,3 +4561,67 @@ anchors are your GLB), but the tooling is now waiting, not TODO.
— a bad night reads as a story you did to yourself. The trap fires as theatre, the Vasilaros — a bad night reads as a story you did to yourself. The trap fires as theatre, the Vasilaros
brief is perfect bait, and the loop is solid. The gap between how the game PLAYS and how it brief is perfect bait, and the loop is solid. The gap between how the game PLAYS and how it
LOOKS is the whole of Sprint 13. LOOKS is the whole of Sprint 13.
[A] 2026-07-18 — 🚪 **SPRINT13 GATE 3 (front door) + GATE 2.5 (camera) LANDED. selftest 339/0/0** (was
335; +4 asserts, all mutation-checked). `lane/a` pushed. main is shippable — the public URL is safe.
**GATE 3 — the front door, since strangers reach this now:**
· **Enter pinned.** The exploit shipped because the rule lived in a keydown closure inside boot(),
behind a canvas — nowhere a test could reach, which is WHY it went out. `enterCommits(phase,
cardOpen)` is a value now, asserted exhaustively over PHASES (a 6th phase is covered the day it's
added, and the default it gets is "may not advance"). Mutation-checked: reverting the guard
reddens forecast/storm/aftermath.
· **Splash card.** A cold visitor used to land on the job sheet — an invoice from a business they'd
never heard of. Now: name, premise, controls, one START. One more `.card`, built on E's letterhead
because the masthead IS the game's name. `boot({splash:false})` skips it for benches and D's
harness.
· **P pause / M mute.** P stops the accumulator (storm only — nothing else has a clock), drains it so
resume gives back no free frame, and clears at every phase change so a pause can't survive into a
night where P is inert. Veil sits under the card layer, over the scene.
· **Touch notice.** `(any-pointer: fine)` not `(pointer: coarse)` — the naive check locks a
touchscreen LAPTOP out of a game it can play, which is worse than the dead canvas. Errs toward
letting people in.
· **Dev fps line** out from under B's anchor panel (both were top-left), and hidden off localhost /
`?dev=1` — a stranger was reading "60 fps · storm 12.3s · debris 4" on the front door.
⚠️ **C — M MUTE IS WIRED BUT NOT ADVERTISED, and it's waiting on ONE line from you.** `createAudio`
holds `master` inside skyfx's closure and exposes only `unlockAudio()`, so main.js has nothing to
turn down. **Please add `setMute(on)` to the skyfx public API** — one line against your master gain
(`master.gain.value = on ? 0 : 1`, or a smoothing ramp if you'd rather). The moment it exists, M
lights up by itself: main.js already asks `typeof sky.setMute === 'function'` at boot, the key is
live, the state is real, and the HUD only advertises M once the tap is there. I deliberately did NOT
ship `sky.setMute?.(on)` behind an advertised key — that's D's night-3 soft-lock with a keycap on it
(`?.()` on a missing method is a silent no-op that looks like a call, on a public URL where the
person pressing M just wants the noise to stop).
**GATE 2.5 — two camera fixes:**
· **Spawn framing.** `spawnYawFor()` in camera.js — points the opening frame at the garden with no
pole through the player, per YARD (off site data), because site_02 spawns elsewhere. Measured, and
wrong twice before right: clearance 0.6 not 1.1 (1.1 is unreachable when a post is 1m from spawn,
and the unbounded fallback swung 105° to a fence), maxOff caps the fallback to a 90° arc. Assert
reproduces the real 105° and pins the cap brings it to 68°.
· **Aftermath cloth-swallow.** The dead sail joins the camera's solid set (`refreshCameraSolids()`,
called from both rebuilds). Verified live: camera solids 9→10 the instant a sail rigs. Added a
`camera.solids` getter so that was checkable — anyone wanting to know what the camera collides with
can now read it.
**For the other lanes, off my files:**
· `camera.js` now exports `spawnYawFor(player, lookAt, obstacles, opts)` and a `cameraRig.solids`
getter. D — if a playtest ever spawns facing wrong on a NEW site, the obstacle list I pass is
`posts + trees + structures` from site JSON; a site with its bed boxed in on all sides gets the
best-available frame, never a throw.
· `main.js` exports `enterCommits`, `accumulate`, `canPlayHere`, `stormsToPreload` (pure, tested).
**GATE 1 — MY TWO RULINGS ARE PARKED, WAITING ON C.** SPRINT13 gate 1.4 routes two decisions to
me and both need C's shadow-geometry diagnosis FIRST: (1) the garden separation target — C proposes,
I rule; the plan's steer is "alive-vs-dead on the wild nights, not 88-vs-83", and I'll hold C to
something a player can FEEL, not just measure. (2) whether `GARDEN_DRAIN` moves once the geometry is
honest — my own main.js comment says the constant was compensating for the rain shadow decaying to
nothing, so once C fixes the MODEL it likely wants lowering back; I'll rule on the number when C
posts the before/after. **C: post the separation target in THREADS before you tune to it** (plan's
instruction), and ping me. Nothing of mine blocks you.
🎮 Front door verified in the running game start to finish: splash → job sheet → prep → storm, P
pauses to a clean veil, the spawn frame is player+bed with the poles beside not through. Screenshot
path in the session. Not asserted, because 339 tests can't see a pole through a head — that's what
gate 4 (John plays) is for, same as the two bugs looking caught last sprint.