Scaffold project and author 14-phase roadmap

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
This commit is contained in:
2026-10-05 17:05:56 -04:00
commit f84c6ebaef
105 changed files with 5752 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
# AGENTS.md — operating rules for this repository
## 1. Read this order, every time
1. `.agents/PLAN.md` — master design. Its **Architectural Anchors** table holds the
**LOCKED DECISIONS**, which are binding on every task and phase.
2. This file.
3. `.agents/phases/todo/<phase>/00_phase.md`, then the task file you are executing.
4. The user story referenced by the phase (`.agents/user_stories/US-XX_*.md`).
5. `.agents/phases/complete/*/00_phase.md` for 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-execution` skill (`run-task.sh`, `run-phase.sh`,
`auto-phase.sh`). Each task runs in a fresh subprocess and must pass
`.agents/validate.sh` before it moves to `complete/`.
- New work is authored by the `phase-authoring` skill as a phase directory — not
implemented straight from chat.
- **Never** modify `.agents/PLAN.md` or 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, no `random` without 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 .` and `uv run pyright` report zero errors.
- [ ] `uv run undercooked-levels validate` passes if any level/content file changed.
- [ ] `README.md` and `AGENTS.md` updated 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 undercooked` serves `http://127.0.0.1:8000/healthz`
with `200` and 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.py`
fails the build on `http://`/`https://`/`//cdn` references in `app/static/` and
templates. CSP is `default-src 'self'` (plus `ws:`/`wss:` same-origin).
- **No new technology** (dependency, framework, service) without explicit written
user permission recorded in the phase's `00_phase.md` and 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 in
`sessionStorage`. 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 bumping `PROTOCOL_VERSION` (PLAN §11).
- **Determinism is a contract.** `sim/step.py` must produce identical state given the
same seed and input script — the replay tests in `tests/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`, subject `phase: <NN_name>`. Child executors
never run `git add`/`commit`/`mv`/`rm`/`stash`. When a human asks for a commit, use
the `git-commit` skill (`--no-gpg-sign`, `LLM-Generated: true` trailer).
- 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`).