Files
undercooked/docs/DESIGN.md
T
ducoterra 6fd83446ec phase: 01_runnable_service_scaffold
**Phase 01 final verification pass — all criteria green**

- Verified (nothing left to implement; all 4 task files already in `complete/`): toolchain lock, typed `Settings`+`.env.example`, debugpy bootstrap, app factory, CLI (`--host/--port/--reload/--levels-dir`, `python -m undercooked`), static shell + `/static`, security headers, no-CDN/doc guards, `validate.sh`.
- `uv sync --extra dev` → exit 0; `uv.lock` present (untracked → harness commits it with the phase).
- `uv run undercooked --host 127.0.0.1 --port 8000` → `curl /healthz` = 200 `{"status":"ok","version":"0.1.0","rooms":0}` (superset of the criterion's body — now documented in README); `curl /` = 200 shell HTML; static css/js 200; CSP/nosniff/no-referrer/DENY on 200, 404 and 405 too.
- `DEBUGPY=1`: port 5678 is held on this machine by the user's own podman dev-container debugpy adapter — **no live change made/killed**; verified via `DEBUGPY=1 DEBUGPY_PORT=5679` → listener opens, `/healthz` still 200; the occupied-port case degraded gracefully as designed (warning, server serves). Bad config → one line + exit 2 ✓.
- `bash .agents/validate.sh` (root **and** `.agents/`) → exit 0: `ruff check .` 0 errors, `pyright` 0 errors, levels-CLI notice-skip (matches README prose), **284 passed** full suite incl. 10 Playwright e2e, coverage **100.00%** (floor 90), repo hygiene clean. `uv run pytest -m e2e` → 10 passed.
- Docs fixed: README Status → phase 01 complete + next pending 02 (was pointing at `todo/01…`), documented real `/healthz` body, precedence now "CLI flags > env > .env > default", added the PLAN §13-promised **How to play** section (honest about shell-only today), Layout rows marked what exists vs phases 02/07. AGENTS.md: recorded the `data:`-URL exemption in the no-CDN rule (test allows it) and matched the session-token rule to the browser's WebCrypto/base64url implementation. Also corrected a stale "session-wide chromium" docstring in `tests/e2e/conftest.py` (fixtures are function-scoped).
- Deviations: no container build run (needs an image pull; not a phase criterion — `Containerfile`/`compose.yaml` reviewed and consistent with `docs/DEPLOYMENT.md`); `HEAD /` is 405 (routes are GET-only by design; health checks use GET). No git index/history commands run.
- Next pending phase: `02_level_content_and_validation`.
2026-10-06 01:51:20 -04:00

2.7 KiB
Raw Blame History

DESIGN.md — architecture and the decisions behind it

Status: stub written by phase 01 · Filled in by phase 09, when the client, the protocol and the first two-player end-to-end story make the prose verifiable.

Purpose

This document explains the shape of the server and the client in prose: which module owns what, why the boundaries are drawn where they are, and the reasoning behind the LOCKED decisions A1–A12 of .agents/PLAN.md §3. PLAN.md states what was decided and stays frozen; this file is where the why lives and where a later phase records the consequences of those decisions once the code proves them out.

The two-line version

One asyncio process holds every room. The browser is an untrusted thin client: it sends inputs, receives snapshots, and interpolates. All truth lives in src/undercooked/sim/, a pure function of (state, dt) with no I/O, no clock and no unseeded randomness, which is what makes headless determinism tests (A5/A21) possible at all. Everything around that kernel — server/ (FastAPI app, WebSocket protocol, room supervisor, in-memory session records) and app/static/ (vanilla ES modules, Canvas 2D) — exists only to move bytes between a player's hands and the simulation.

Module map (as it stands after phase 01)

Path Owns Filled in by
src/undercooked/config.py the only read of the environment → typed, validated Settings phase 01 (done)
src/undercooked/debug.py DEBUGPY-gated debugger bootstrap (A18) phase 01 (done)
src/undercooked/server/app.py app factory, /healthz, /, /static, startup logging phase 01 (done)
src/undercooked/server/headers.py CSP + the four other security headers on every response phase 01 (done)
src/undercooked/sim/ game truth: model, step(), interactions, scoring, hazards phase 03–04
src/undercooked/server/room.py, rooms.py one match, its FSM and tick task; the registry that owns all of them phase 05–06
src/undercooked/server/protocol.py versioned wire messages, whitelists, inbound validation phase 06
src/undercooked/server/sessions.py bounded in-memory progression records (A6: no database) phase 05
app/static/js/ screen state machine, net buffer/interpolation/prediction, renderer, HUD phase 07–08

What this document will contain when it is filled in

The request/response and tick data flow with real timings, the room lifecycle state machine as built, the arbitration rule for contested resources, the client prediction and reconciliation model, and one worked paragraph per anchor in A1–A12 covering the alternatives that were rejected and the measured cost of the choice that was taken.