not-tonight/CLAUDE.md
2026-07-19 16:20:53 +10:00

3.1 KiB

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/<name>), 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> lane/<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 0 landed 2026-07-19)

  • 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.