# NOT TONIGHT — instructions for Claude sessions in this repo Papers, Please-style bouncer sim. Browser game: Phaser 3 + TypeScript (strict) + Vite + Vitest. You are probably an **Opus 4.8 lane executor** — this repo is built by parallel "lanes" coordinated through files, reviewed by Fable. ## Before writing any code 1. Read `LANEHANDOVER.md` — find the latest REVIEW block and any block for your lane. Review instructions override lane files. 2. Read your lane file in `lanes/` (John/Fable will tell you which lane you are; if not stated, ask — do not guess). 3. Skim `docs/GAME_DESIGN.md`, `docs/BUILD_PLAN.md`, and read `docs/CONTRACTS.md` fully. Contracts are law. ## Hard rules - Work ONLY on your lane branch (`lane/`), only in your owned directories (BUILD_PLAN.md §2) + your tests + LANEHANDOVER.md. **Never merge or push main.** - Parallel lanes on one machine: use a git worktree (`git worktree add ../not-tonight- lane/`), don't switch branches in a shared checkout. - Don't modify `docs/CONTRACTS.md`, `src/core/`, or `src/data/types.ts` after Phase 0 — file a CONTRACT CHANGE REQUEST in the handover instead, stub locally with `// TODO(contract):`, keep working. - No `Math.random()` — SeededRNG streams only. No cross-lane scene imports. Game logic in pure tested functions (`src/rules/`, per-lane logic modules); scenes stay thin. - End EVERY session: `npm run lint && npm run build && npm test` clean, append a SESSION block to `LANEHANDOVER.md` (template in that file), commit, push your branch. An unpushed session didn't happen. - Remote is Gitea: `ssh://git@100.71.119.27:222/monster/not-tonight.git`. ## Dev notes (Phase 2 landed 2026-07-19) - The game boots into the NIGHT (Thu→Fri→Sat run, saved). Controls: click the rope/verdicts at the door; click the venue doorway to step inside; on the floor WASD + E (E at the entry exits back to the door). While you're inside, Kayden works the door — badly, on purpose. - Dev routes: N night · P parade · F floor demo · J juice demo (+ same-named URL hashes) · M toggles the MIX DESK overlay anywhere (ear-pass rig). `localStorage.removeItem('not-tonight-save')` for a clean run. - Audio unlocks on first click (TechnoEngine + location filter). Before that a StubBeatClock keeps `beat:tick` honest. - Venue spaces/props/lights are DATA (`venueMap.PROPS`/`LIGHTS`, plan in docs/SPACES.md). Generated art drops into `public/props/` via `tools/props_import.py` + manifest — placeholders render until then. ## Dev notes (Phase 0, still true) - Gate: `npm run lint && npm run build && npm test` — all three must pass before any push. Dev server: `npm run dev` (port 5199, config in `.claude/launch.json`). - Key APIs (read the source, it's short): `core/EventBus` (`on` returns an unsubscribe fn), `core/SeededRNG` (`rng.stream(name)` — NEVER `Math.random`, eslint blocks it), `core/GameClock` (feed real ms via `update(delta)` from your scene; `ageOn(dob, nightDate)` is the ID math), `core/meters` (emit `meters:delta`, never mutate NightState), `patrons/generator` (`generatePatron(ctx, clockMin, override?)`), `patrons/dollPlan` (pure, testable) + `patrons/doll` (`renderDoll(scene, patron, pose)` → texture key), `rules/drunk` (`drunkStage`). `StubBeatClock` needs an owning scene calling `update(delta)` each frame. - **Verifying in Claude's browser preview pane:** the pane tab reports `document.hidden = true`, so Chrome suspends rAF and the Phaser loop freezes at frame 0. Drive frames manually via the exposed debug handle: `const b = performance.now(); for (let i = 1; i <= 900; i++) window.game.loop.step(b + i * 16.67)` then screenshot/inspect. In a normal browser the game runs fine. ## Tone guard The game is Aussie hospitality satire — affectionate, funny, with a moral undertow (design doc §4.3). If content you're writing reads as mean-spirited rather than absurd, soften toward absurd. ## Deploy (live since 2026-07-19) - Live at **https://monsterrobot.games/not-tonight/** (botchat VPS, forum-nginx bind mount — host path `/home/humanjing/monsterrobot.games/games/not-tonight/`, `/not-tonight` is a symlink to it; landing tile in `../index.html`). - Ship: `npm run build && rsync -az --delete dist/ humanjing@100.71.119.27:/home/humanjing/monsterrobot.games/games/not-tonight/` then cache-busted curl to verify. Hashed asset names make CF cache a non-issue; index.html is text/html (uncached by CF default). - Cover art: `covers/nottonight.jpg` + `_riso.jpg` (MODELBEAST flux_local seed 4208 + PIL stamp text/riso pass — recipe in LANEHANDOVER Phase-3 deploy note).