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>
10 KiB
10 KiB
LANE HANDOVER LOG
Coordination file between lanes (Opus 4.8 execution sessions) and the reviewer (Fable). Append-only — never edit or delete previous blocks. Newest block at the BOTTOM. Push after every session so the reviewer can see state remotely.
Protocol
- Before starting work: read this whole file + your lane file in
lanes/+docs/CONTRACTS.md. Check for a REVIEW block addressed to your lane — it overrides your lane file where they conflict. - Work only inside your lane's owned directories (see BUILD_PLAN.md §2).
- End of session: append a SESSION block (template below), commit, push your
lane/<name>branch. - Never merge to main — the reviewer does that.
- Blocked on a contract ambiguity? Don't stall and don't silently change the
contract: stub locally with
// TODO(contract):, log a CONTRACT CHANGE REQUEST below, keep moving.
SESSION block template
---
### SESSION — <LANE-NAME> — <date> <time>
**Branch/commits:** lane/<name> @ <short-sha>..<short-sha>
**Done:** bullet list of what actually works now (be honest — "renders but untested" ≠ "done")
**How to see it:** exact command + what to click (e.g. `npm run dev` → press D → deny 3 patrons)
**Tests:** X passing / Y failing (name the failures)
**Broke / known-wonky:** anything you left imperfect
**Contract change requests:** none | numbered list with justification
**Questions for reviewer:** none | numbered list
**Next session should:** bullet list
REVIEW block template (reviewer only)
---
### REVIEW — Fable — <date>
**Reviewed:** <lanes/commits>
**Merged to main:** <yes/no + sha>
**Contract decisions:** rulings on any open change requests
**Instructions for <LANE>:** what to do next / what to fix first
(log starts below)
SESSION — LANE-0 (executed by Fable, reviewer) — 2026-07-19 16:30
Branch/commits: main (reviewer executed Phase 0 directly; no lane branch) Done:
- Vite + strict-TS + Phaser 3.90 + Vitest + ESLint scaffold;
Math.randombanned by lint rule. - All CONTRACTS.md types verbatim in
src/data/types.ts. core/: SeededRNG (mulberry32, named streams), typed EventBus (snapshot-emit semantics), GameClock (real-ms driven,ageOnbirthday math incl. leap-day), Meters (single-writer, hype multiplies positive vibe only, clicker drift), StubBeatClock (128 BPM), versioned corrupt-safe localStorage save.data/: outfit vocabulary (+PALETTE), 9 archetypes, 10 contraband items, 17 Dazza texts (8 rule-bearing with ruleIds).patrons/: generator (fake-ID tell system, DOBs clustered near the 18 boundary, late-arrival drunk drift), puredollPlan+ PhaserrenderDoll(3 poses, drunk tells baked), regulars' memory store.rules/drunk.ts: drunkStage thresholds.- ParadeScene demo: rainy street, kebab-shop + venue dressing, 24-walker parade with ID cards rendered from the same data, SPACE reseed / D toggle cards.
- 49 tests green; lint/build/test gate clean.
How to see it:
npm run dev→ http://localhost:5199 (in the Claude preview pane, drive frames per CLAUDE.md "Dev notes"). Watch for analmost18label walking under an ID card claiming 18+ — that's the whole game in one image. Tests: 49 passing / 0 failing. Broke / known-wonky: - ID cards overlap when the footpath is crowded — cosmetic, parade-only.
window.gamedebug handle exposed in main.ts (deliberate, for pane-driving).- Spec deviations: doll renderer split into
dollPlan.ts(pure) +doll.ts(Phaser) — better than spec'd, plans are pixel-testable without a GPU; added a 5th fake-ID tell (expired real card, no boolean tells); no MainMenu scene (Boot → Parade direct) — menu is Phase 2 shell work. Contract change requests: none (contracts implemented as written). Questions for reviewer: n/a (self-reviewed). Next session should: Phase 1 lanes are OPEN — see REVIEW block below.
REVIEW — Fable — 2026-07-19
Reviewed: Phase 0 (self-executed; verified via test gate + in-browser parade). Merged to main: yes (direct). Contract decisions: CONTRACTS.md now frozen. Extension requests via this file. Instructions for Phase 1 (LANE-DOOR, LANE-FLOOR, LANE-JUICE):
- Branch from current main into
lane/door/lane/floor/lane/juice(worktrees if sharing a machine). Read your lane file's new "Phase-0 reality" section FIRST — it corrects the original spec where the landed code differs. - LANE-DOOR is the critical path; if only one Opus session is available, run DOOR first, JUICE second, FLOOR third.
- Every session ends with the gate + a SESSION block here + a push. Fable reviews on request ("review the lanes").
SESSION — LANE-FLOOR — 2026-07-19 18:50
Branch/commits: lane/floor @ 9805ff8..(this commit) — branched from main, worktree ../not-tonight-floor
Done:
FloorDemoScene(menu key F from Parade): 80x45-tile venue (1280x720, 2x screen, camera-follow), 44-patron crowd sim, flashlight cone, UV mode, all three v0.2 infractions + the stamp check, playable end-to-end.- Pure logic, all tested:
venueMap(programmatic layout + anchors + stalls),cone(vision maths),player(heavy accel + wall slide),crowdSim(archetype-weighted anchor wandering, clock-driven intoxication drift, beat-driven dance bob, stall double-occupancy, escort-out),bangJudge(nearest-beat-boundary timing),escalation(5x3 cut-off matrix),patDown(pocket layout + scoring). - Phaser layer:
FloorView(baked tile texture, silhouette/detail split with a cone GeometryMask, neon glow pools, UV stamp dots) + three overlays (CutOffOverlay,StallOverlay,PatDownOverlay). - All floor dialogue in
src/scenes/floor/strings.ts(see deviation #1). How to see it:npm run dev→ press F at the Parade → WASD patrol, TAB UV, E interact, SPACE bang, ESC cancel, R reseed. Walk into the dark until the torch finds someone. In Claude's preview pane drive frames per CLAUDE.md. Tests: 169 passing / 0 failing (49 pre-existing + 120 new floor tests). Verified in-browser, not just green: cut-off (correct rung ejects, one rung too soft on a maggot lets them stagger free), escort walks to the exit and logs an ejection, stall busts on 3 on-beat bangs and never busts on 8 off-beat ones, pat-down bins an item (+2 vibe, right flavour line) and leaks it on timeout (-1.5), UV shows the stamp glow and marches an unstamped patron out with a Kayden radio line. Broke / known-wonky: - Two bugs the test suite could not catch, both found by running it and both fixed:
- Ambient meter collapse. The bump penalty had a per-agent cooldown but no
sim-wide bound, so leak rate scaled with crowd size — an unattended venue
pinned vibe to 0 and aggro to 100 by ~1AM before the player touched anything.
Added a sim-wide gate (
BUMP_GATE_MS); an unattended night now drifts 50→27 vibe / 0→34 aggro. Regression test added. - Frozen beat behind modals.
beat.update()sat below the overlay early-return in the scene loop, so opening the stall minigame stopped the beat clock and the rhythm game could never receive a tick. Clock + beat now run regardless of overlays.
- Ambient meter collapse. The bump penalty had a per-agent cooldown but no
sim-wide bound, so leak rate scaled with crowd size — an unattended venue
pinned vibe to 0 and aggro to 100 by ~1AM before the player touched anything.
Added a sim-wide gate (
- Dwell times raised (2.5-9s → 7-22s): at the old values ~68% of the crowd was permanently in transit and the bar/dance floor read as empty.
- Tile palette + darkness alpha retuned; at the original values the venue multiplied down to near-black and you could not tell a dance floor from a wall.
- Light2D dropped in
FloorView(documented in-file) — the pipeline swap fights the cone GeometryMask, and the mask is the actual gameplay. The look is carried by a darkness sheet + additive cone + neon glow pools instead. GeometryMaskon a container is a WebGL-first path; fine underPhaser.AUTO, would misbehave if anything ever forced the Canvas renderer.- The 16x16 top-down doll reads as a coloured ring from above (dark hair over
outer-layer shoulders). Correct per Phase 0's
dollPlan, just noting it. - Stall interiors are deliberately sealed (
stallwalkable,stallDoornot), so patrons are moved in/out viaStallDef.inside/.doorrather than pathed. Contract change requests:
incident:logevent.coreexposes no incident API andEventMapis frozen, soFloorDemoSceneappends toNightState.incidentsdirectly (marked// TODO(contract):). Three lanes writing that array directly is exactly the shared-mutable-state fight the bus discipline exists to prevent, and Phase 3's inspector/audit needs a single writer. Requesting'incident:log': IncidentRecordplus a handler incore/meters.ts.- Aggro has no decay.
Metersonly ratchets aggro upward, so it is a one-way trip to 100 over a long night. Ambient pressure is now bounded (above), but a slow downward drift on a quiet floor would make the meter a state rather than a countdown. Flagging as a design/contract question, not patching it unilaterally. Questions for reviewer: - Floor strings placement. CONTRACTS.md §7 says Aussie dialogue lives in
src/data/strings/, butdata/is LANE-0's and lanes commit only in their own dirs. Parked insrc/scenes/floor/strings.ts. Move it tosrc/data/strings/floor.tson your word — it is a pure file move. .gitignoregap (affects every lane using a worktree). It listsnode_modules/with a trailing slash, which does not match a symlinkednode_modules— worktrees that symlink it show?? node_modulesas untracked. I staged paths explicitly, but a lane runninggit add -Awould commit a symlink pointing at an absolute path on John's machine. One-char fix (drop the slash); left alone since.gitignoreis not in my lane.- Escalation numbers (clean +4 vibe, scene -3/+5 per rung of overreach) are deliberately small so aggression never feels rewarded. Confirm the balance intent before LANE-DOOR mirrors it at the rope. Next session should:
- Phase 2 integration: replace the self-populating spawner with the real admitted-patron
list from LANE-DOOR (the scene already only ever consumes
Patron[]). - Swap
StubBeatClockfor LANE-JUICE'sTechnoEngine—StallOverlaytakesbeatIntervalMsin its constructor and readsbeat:tickoff the bus, so it should be a one-line change. - Fight-brewing infraction (design §3.2) is specced but NOT built — the only v0.2 floor mechanic still missing.
- Replace
HudStubwith LANE-JUICE's real meter HUD.