- scenes/floor/sweep.ts: the closing sweep extracted behind an accessor host.
Scene 2100 -> 1981 lines, behaviour verified identical.
- data/roleBeats.ts: 18 beats across 6 roles x 3 nights, landing in the venue
group chat. The glassie is spoken about; the HoS is spoken to.
- dollPlan(p, pose, frame): 4-frame idle on the queue pose. NOT pre-rendered
sheets — those would hide the outfit tells the dress code convicts on, which
design 4.1 forbids; the pre-render path must go UNDER the outfit rects.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
authority (eject/pat-down/cut-off/busts) and stations (taps/decks) are now per
role. Gated verbs become 'radio it in' — reusing the staff competence roll, so
~2 in 3 calls bring somebody over and the rest just hang in the air. Doorgirl
keeps floor authority on purpose: that loop is the shipped game.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- pourAdjust pays hype: clean 0.03, heavy 0.05, short nothing. The crowd-pleaser
is also what walks the room toward the RSA line — same lever, both ways.
Role pay gap closes from ~$180 to $45.
- breachSeenChance(inspectorWatching): 0.35 alone, 0.9 with a lanyard in the room
(design §6). Corrects SOLO-19: detection was never the weak part — dedupe caps
strikes at one a night and 0.35 already saturates across several pours.
- Sim harness was missing the 3 AM audit and under-reporting late-night strikes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A bar shift logs ~26 serves and buildReport takes the top 3 with no dedupe, so
bartender nights filed three identical generic pour questions. Weights now price
frequency as well as interest, and the loaded kinds have real questions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
vitest does not typecheck, so these passed under `npm test` while `npm run build`
failed. The gate is lint AND build AND test for exactly this reason — run it in
that order after touching tests, not before.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- DJ post stood the player inside the sealed djbooth (same class as the taps
softlock): every direction blocked, and since the sweep needs you to walk to
the entry, the night could never be paid. DECKS_SPOT moves to the booth's west
counter, which is where the map comment always said the gear faces from.
- Role stations are now DATA (venueMap POSTS/PROBES) with tests/floor/stations.test.ts
pinning the whole bug class: posts must be walkable AND escapable, probes must
have somewhere walkable within interact range. Written as a MOVEMENT test, not
a tile-name test — the question is whether the player can leave.
- DoorScene.busy is released by a delayedCall on the scene clock, and stopping a
scene discards pending events, so a ruling in the last 2.6s of a night left it
stuck true: a dead door — no rope, no verdicts, no way inside — for the rest
of the run. Now reset per night, along with seenCards/kebab/franko/retiredRules.
- Bar WATER and CUT OFF never settled the patron, so watering a maggot from
behind the taps left them ticking as unhandled and re-logging every grace
period: the call that fixed the problem kept being charged for it.
- Every bar verb logged as 'barServe', putting drinks in the record that were
never poured. One kind per verb; the report system takes unknown kinds by design.
- carpetMsFor() is consumed on read: a regular parked, recalled and knocked back
came round again later and was paid the entrance dividend a second time.
- Sweep state joins resetNightState() so an interrupted floor score cannot carry
its cash, or its previous-night pay callback, into the next night.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
toggleTaps() teleported the player to TAPS_FRONT on exit, and TAPS_FRONT was
tile (47,6) — inside venueMap's stool rect, which is not in WALKABLE. Verified
in the live build: after handing the taps back the player sat at (760,104) and
W/A/S/D each moved them exactly 0px, for the rest of the night. The bartender
roster role mounts the taps automatically, so one keypress ended the run.
Exit no longer teleports at all: you stay behind the bar and walk out around
its east end, which is both walkable (verified end to end) and how staff
actually leave a rail. TAPS_FRONT moves to row 7 so the approach probe sits on
the punter side rather than inside the furniture.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
applyLocation() only moved flags — it never announced the move. FloorDemoScene
latches `present` when it binds the night context, which (now the night preloads
every prop texture) happens BEFORE the initial applyLocation runs, so every
floor-start shift — bartender, glassie, DJ — came up visible but completely
inert, with the audio filter still in door mode. The night now emits
night:phaseChange and audio:location for the starting post.
Phaser reuses scene instances across restarts, so class-field initialisers run
once per GAME, not once per night: a DJ shift left djMode set and the next
night's floor booted with movement locked and the keyboard eaten, last night's
puddles kept slipping people through destroyed sprites, and the handled-patron
sets marked fresh arrivals as already dealt with. resetNightState() now wipes
all of it in create().
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
showScene() stopped 'Door' (it was in the stop list) and then declined to
restart the already-active 'Night', leaving an unplayable black screen with no
way back. Route only ROOT scenes, and restart the target when it is already
running so N means 'fresh night'. NightScene.teardown() now stops Door and
Floor, making real the ownership main.ts always claimed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ported from claude/epic-faraday-332679 (6c5889c) — merge was permission-blocked,
same diff applied directly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Phase-1 debt was 'verified by ear: NOTHING'. I still can't hear, so this
lane builds the instrument that lets someone who can tell me what's wrong, and
makes their answer a diff instead of a conversation.
- TechnoEngine drives every voice from the live TrackMix and the arc, instead of
module consts. Behaviour-identical at DEFAULT_MIX (verified constant by
constant). Voices whose effective gain rounds to silence are skipped, not
clamped — at 9PM the bass builds zero nodes rather than ~8/sec of silence.
setMixValue quantises integer paths, so what formatMix dumps is what played.
- MixDeskScene: solo/mute per voice with a per-voice listening prompt, every
mix param on log-mapped sliders, an arc scrubber (audition 1AM at 9PM), and
DUMP MIX -> a pasteable TrackMix literal. Restores the arc pin and solo state
it found on close, rather than forcing a default — the demo deliberately pins
PEAK and the desk was silently un-tuning it.
- Ambience: rain outside, crowd murmur inside, kebab-shop fluoro hum. On the SFX
bus, not the music lowpass — that filter models the PA being behind a wall,
and rain is not behind anything. Headcount comes off door:verdict admits and
floor:ejection, NOT door:clicker: the clicker is the player's claimed count and
its divergence from reality is the mechanic, so the room would sound like the
lie. Verified live: 120 + 2 admits - 1 ejection = 121, clicker ignored.
- JuiceDemoScene pins the arc at PEAK on entry. GameClock runs 360 game-min in
13 real min, so an unpinned demo opens on kick+hat and anyone judging the bass
would conclude it's broken.
- Phone thread scrolls with visible-window culling and a binary-searched range,
a history cap, and a 'new below' cue when a text lands off-screen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 3 deliverables 1-4. The door now has something to read rather than a
checklist to tick.
The guest list clipboard shares the desk's left panel with Dazza's rules via a
tab, because there is no spare space at 640x360 and the tradeoff is honest:
checking a name costs you a beat of queue time. The mechanic is measured rather
than asserted — at the generator's real claim rates an exact match is a coin
flip, a near match leans legit, and no match leans liar. Owner's-mate patrons
now drop Terry's name, so the trap has a tell that isn't just confidence.
Scripted encounters (four, three a night) arrive ahead of the arrival curve and
speak in beats. Their consequences are real and the game never says which call
was right — no meter, no chime, no narrator.
The inspector reads as a boring punter; admitted, he converts any live breach
from a 3 AM audit finding into an immediate strike. Denied, nothing happens
ever. Verified in play: a night ended LICENCE PULLED with two of three strikes
his.
The incident report sits between the summary and the next shift. Filed accounts
go to pastReports so Phase 4 audits what the player claimed; NightScene no
longer dumps the raw truth there, which had nothing to catch anyone out with.
Also closes my own Phase-1 finding: judge() now reads intoxication and
contraband. Refusing someone who cannot stand up used to score identically to
refusing a clean punter, so the game's most legible signal was worth nothing.
That gap was also what made quietBeer's humane option secretly optimal on both
meters — the one thing design 4.3 forbids.
518 tests (was 413). Played a full 3-night run: all three nights reached 3 AM,
heat carried 1 -> 2 -> 2. The Phase-1 economy findings are all still true and
this lane did not change them — vibe pinned at 97-98 every night and aggro sat
at 0. Details in LANEHANDOVER.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Builds the last missing v0.2 floor mechanic (design §3.2). A fight is not a
modal: two patrons square up, a 7s fuse burns through five escalating shove
rungs, and you stop it by physically standing between them — it spends the
floor's only real resource, where you are. The tell (shoving silhouettes +
pulsing marker) deliberately reads OUTSIDE the torch cone, because spotting it
is the gameplay.
Adversarially reviewed the whole lane, then hand-adjudicated the findings. Six
real defects fixed, several in code from round 1:
- PatDownOverlay compared 'Escape' against the scene's uppercased 'ESCAPE', so
ESC was dead there while the on-screen hint promised it worked.
- The spawn cap counted departed agents (CrowdSim keeps them so find() works),
so every ejection permanently shrank the venue and the floor drained.
- FloorView's sprite cache is keyed by patron id and outlived resetRun, while
_resetPatronSerial recycles ids from p0 — reseeding gave the new crowd the
old run's dolls.
- escalation's per-incident `resolved` was consumed as a permanent per-patron
`handled`, retiring a correctly-watered `loose` patron for the whole night
while they kept drinking. Now settles per stage and re-arms when they worsen.
- Stall busts retired the captured occupant list, but the crowd keeps simulating
behind the modal. Re-queries at resolution.
- Stall infractions put a stall id in the frozen `patronId` field.
Also wired teardown to Phaser's SHUTDOWN event; a `shutdown()` method is never
called, so leaving the floor leaked the view, sim, overlays and bus listeners.
Fight balance was measured, not guessed: at a 20s cooldown an idle night brewed
28 fights and 28 heat strikes against a run cap of 3. Now 150s (~5/night) with a
deferred heat strike, matching §3.2's "possible Heat". An idle night ends at
vibe 0 / 5 strikes; an attentive one at vibe 54 / 0 strikes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Phase-1 engine baked every level, pitch and envelope into module consts, so
nothing could be tuned without an edit-reload cycle. Three things need them
live: the ear pass (a human turns a knob and hears it on the next beat), the
night arc, and the Phase-4 DJ role, whose control panel is specified as this
exact surface (BUILD_PLAN §4).
- mix.ts: TrackMix + DEFAULT_MIX (reproducing the Phase-1 values exactly, so
lifting them changes no sound), MIX_RANGES bounds, and formatMix() — which is
what turns a tuning session into a diff instead of numbers that die with the
tab.
- arc.ts: the night's shape as per-voice gain scalars. Thin kick at 9PM, bass
in once there's a crowd to carry it, everything at midnight, low end pulled
first at Last Drinks. Pure keyframes, so it's testable and the DJ role can
drive the same surface by hand.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>