Browser co-op kitchen game: FastAPI + native WebSockets, server-authoritative 20 Hz simulation, in-memory anonymous lobbies, data-driven YAML levels, Canvas 2D vanilla ES-module client with locally authored SVG art and no external CDNs. .agents/PLAN.md records the LOCKED decisions; .agents/user_stories/ holds the 12 flow contracts; .agents/phases/todo/ breaks them into 14 sequential, test-gated phases (71 task files), each leaving the app launchable and the suite green. LLM-Generated: true
6.3 KiB
6.3 KiB
AGENTS.md — operating rules for this repository
1. Read this order, every time
.agents/PLAN.md— master design. Its Architectural Anchors table holds the LOCKED DECISIONS, which are binding on every task and phase.- This file.
.agents/phases/todo/<phase>/00_phase.md, then the task file you are executing.- The user story referenced by the phase (
.agents/user_stories/US-XX_*.md). .agents/phases/complete/*/00_phase.mdfor what already exists (do not re-build it).
2. The phased protocol
- Phase state lives in files, not chat. Pending work:
.agents/phases/todo/NN_name/(00_phase.md+NN_task.md). Finished work:.agents/phases/complete/. - Execution is owned by the
phased-executionskill (run-task.sh,run-phase.sh,auto-phase.sh). Each task runs in a fresh subprocess and must pass.agents/validate.shbefore it moves tocomplete/. - New work is authored by the
phase-authoringskill as a phase directory — not implemented straight from chat. - Never modify
.agents/PLAN.mdor anything under.agents/phases/complete/. - Ask the user before editing an existing file in
.agents/phases/todo/. - Do not renumber or reuse phase/task numbers.
3. LOCKED DECISIONS (summary — the table in PLAN.md is authoritative)
- Python 3.12+ / uv / FastAPI / native WebSockets (
uvicorn[standard]). No Socket.IO, no SSE, no WebRTC, no REST for live game traffic. - Server-authoritative fixed-tick simulation (20 Hz) with client interpolation.
The browser never decides anything true.
src/undercooked/sim/is pure: no FastAPI, no asyncio, no I/O, no wall-clock, norandomwithout the round RNG. - No database, no ORM, no Alembic. Live state lives in the room supervisor; results/scoreboards live in memory and die with the process. User-approved deviation from the default FastAPI stack.
- Data-driven content. Levels/recipes/hazards come from YAML in
levels/loaded through validated Pydantic models. No hard-coded level or recipe lists in Python. - Client: Canvas 2D + vanilla ES modules + this repo's own SVG art. No bundler, no framework, no CDN, no analytics, no external fonts.
- Tests: pytest + Playwright. One Playwright suite per user story; multi-player stories use two browser contexts against one room.
- Quality gate: ruff + pyright + full pytest, >90% coverage on new/modified code, entire suite 100% passing before a phase may complete or commit.
4. Definition of done (every task)
- Task's own work exists at the named paths and is importable/runnable.
- Tests written for the task's logic (unit for pure logic, integration for rooms/protocol, e2e for user-visible flows) and passing.
- Full project suite passes (
uv run pytest), coverage gate met — including failures the task did not cause: fix them, do not skip, xfail, or delete tests. uv run ruff check .anduv run pyrightreport zero errors.uv run undercooked-levels validatepasses if any level/content file changed.README.mdandAGENTS.mdupdated when the change is user-visible (setup, commands, config, protocol, features, conventions). Every phase's final pass re-reviews both in full, project-wide and fixes inconsistencies, including pre-existing ones.- App still launches:
uv run undercookedserveshttp://127.0.0.1:8000/healthzwith200and the client at/. - No behavior regression in any completed phase.
5. Hard rules
- No external CDNs or remote assets.
tests/unit/test_no_external_assets.pyfails the build onhttp:///https://///cdnreferences inapp/static/and templates. CSP isdefault-src 'self'(plusws:/wss:same-origin). - No new technology (dependency, framework, service) without explicit written
user permission recorded in the phase's
00_phase.mdand signed off into PLAN.md. - The simulation is the single source of truth. Client-side values are guesses.
Never trust inbound coordinates, scores, timers, or level ids — validate every
inbound field against the room's state (
server/protocol.py). - Anonymous by design. No accounts, no emails, no cookies with personal data.
Identity = a random per-browser token (
secrets.token_urlsafe) held insessionStorage. Names are sanitized, length-capped, non-authoritative. - Frame and rate limits are enforced, not suggested:
max_message_bytes,max_inputs_per_s, room caps, and message-type whitelists per room state. - Protocol changes need a version bump: editing a wire message means editing
src/undercooked/server/protocol.py,docs/PROTOCOL.md, and the client in the same task, and bumpingPROTOCOL_VERSION(PLAN §11). - Determinism is a contract.
sim/step.pymust produce identical state given the same seed and input script — the replay tests intests/sim/are the guard. - ntfy is outbound-only. Never use it to ask questions or wait for approval. If a task is ambiguous, make the best judgment consistent with PLAN.md, or stop and let the user rewrite the task.
- Commits are made by the pipeline harness (
PHASE_COMMIT=1), one atomic commit per completed phase,--no-gpg-sign, subjectphase: <NN_name>. Child executors never rungit add/commit/mv/rm/stash. When a human asks for a commit, use thegit-commitskill (--no-gpg-sign,LLM-Generated: truetrailer). - Do not commit
.env, logs, coverage output,.agents/phase-sessions/,.agents/pipeline.log, or.agents/reports/(all gitignored).
6. Where things go
| Change | Location |
|---|---|
| Pure game rules (movement, stations, orders, scoring, hazards) | src/undercooked/sim/ |
| Level/recipe content | levels/*.yaml |
| Rooms, lobby lifecycle, session records | src/undercooked/server/rooms.py, room.py |
| Wire messages + validation | src/undercooked/server/protocol.py, docs/PROTOCOL.md |
| HTTP app, static serving, headers | src/undercooked/server/app.py |
| Renderer, HUD, input, interpolation | app/static/js/, app/static/assets/ |
| User-visible flow contract | .agents/user_stories/US-XX_*.md |
Tests mirror that: tests/unit/, tests/server/ (HTTP routes and stores),
tests/integration/ (live uvicorn + WebSocket clients), tests/sim/ (headless
determinism and replays), tests/e2e/ (Playwright, named test_usNN_*.py, marked e2e).