Playable start to finish: 9 PM to 3 AM, patrons arrive on a curve, you work the
rope, inspect them, and rule. Dazza texts escalate the dress code through the
night, deferred consequences land when you can no longer argue, and the night
ends in a summary or one of two fail states.
Rules engine (src/rules/, pure, no Phaser):
- dressCode: 8 rules whose text/activeFrom are read out of data/strings/dazza.ts
by ruleId, with a load-time throw if a rule and its announcing text disagree
- idCheck: all ID date maths, built on core/GameClock's ageOn
- judge: the single scoring function, every magnitude named in JUDGE_TUNING
- sobriety: challenge content and the drunk-performance model
Scene layer (src/scenes/door/): NightScene shell, DoorScene, pure QueueManager +
arrivalCurve, patron-up inspection view, ID card, phone, dress-code card,
sobriety modal, stamp, summary.
Tests 49 -> 229.
Ten bugs found by playing it rather than by testing it, including one patron
being ruled eleven times (state was mutating inside a tween callback; it now
settles synchronously), a correctly-played night still rioting at 1:37 AM, and
28% of patrons being deniable for a logo the renderer never drew.
Contract change requests CCR-1..3 are in LANEHANDOVER.md. src/data/types.ts,
src/core/ and docs/ are byte-identical to main; the two additive extensions are
declare-module augmentations. src/main.ts is the one file touched outside the
lane (8 lines, to boot into the night) and is flagged for the reviewer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FloorDemoScene (F from Parade): an 80x45-tile venue you patrol in the dark with
a flashlight cone that reveals detail, plus all three v0.2 infractions and the
UV stamp check. Self-populates from the patron generator; consumes Patron[] only,
so Phase 2 can hand it the real admitted list.
Pure, tested logic (venueMap, cone, player, crowdSim, bangJudge, escalation,
patDown); Phaser layer stays thin (FloorView + three overlays). 120 new tests,
169 total.
Two bugs found by running it rather than by the suite, both fixed:
- bump penalties had a per-agent cooldown but no sim-wide bound, so the leak rate
scaled with crowd size and an unattended venue pinned vibe to 0 / aggro to 100
by ~1AM. Sim-wide gate added, with a regression test.
- beat.update() sat below the overlay early-return, freezing the beat clock behind
modals and making the stall rhythm game unwinnable. Clock and beat now always run.
Also retuned dwell times and the tile/darkness palette for readability, and
dropped Light2D in favour of the cone mask it was fighting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Testable-in-node math extracted ahead of the WebAudio/Phaser layers:
- audio/scheduling.ts: look-ahead beat grid (dueBeats never repeats or skips a
beat however ragged the timer wakes; burst-capped for suspended tabs)
- audio/filterCurve.ts: the location filter's exponential cutoff ramp — the
door-opening sweep, in frequency-space so it blooms late like a real door
- ui/style.ts: chunky pixel widget kit sharing the doll PALETTE
- ui/typo.ts: deterministic drunk-typo/typing/wobble renderer (seeded, no
Math.random)
- ui/juiceEvents.ts: EventMap extensions via declaration merging, so
door:phoneTheatre types correctly without editing frozen data/types.ts.
TODO(contract) — CONTRACT CHANGE REQUEST filed in LANEHANDOVER.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>