phase: 07_canvas_client_and_hud

**Final report — phase 07 final verification pass**

- **Verified:** all 5 tasks present in `complete/`; app boots (`/healthz` 200, `/` 200, static + atlas + `/api/levels` served); `docs/CLIENT.md` module map matches the on-disk `app/static/` tree; no code defects found.
- **Tests/lint/coverage:** `bash .agents/validate.sh` → ruff clean, pyright 0 errors, `undercooked-levels validate` pass, **full pytest 1838 passed, 97.21% coverage (floor 90%)**, slow sim stage 2 passed, repo hygiene clean. `uv run pytest tests/e2e -q` → **53 passed** (all 8 suites added this phase).
- **Criteria:** (1) two tabs create→join→ready→start→serve one dish, keyboard only — green via `test_us01/02/03/04` ✔; (2) e2e green ✔; (3) canvas non-blank, pixels change after a move — `test_us04_render_smoke` ✔; (4) HUD score within 200 ms of the `served` event — asserted, green ✔; (5) zero console errors + zero off-origin requests — `test_atlas_loads` / `test_us01_landing_surface` ✔; (6) `docs/CLIENT.md` matches layout ✔; (7) 100% suite + 97.21% ✔; (8) docs pass below ✔; (9) no regression in completed phases (all prior suites green) ✔.
- **README.md fixed:** status still said "next pending phase is 07" → now 07 complete, 08 pending; the phase-07 client text buried in the phase-05 bullet → its own "canvas client, art, HUD (phase 07)" bullet; stale "What does not exist yet" (claimed movement/controls missing) → rewritten to phases 08–14; "once the room flow ships" → "live in the browser today"; Layout `app/static/` "renderer arrives with the rest of phase 07" → full shipped-client map; Layout `docs/` "CLIENT lands with phase 07" → `CLIENT.md` listed; added `gen_art_index.py` Layout entry.
- **AGENTS.md fixed:** "the eight screens" (7 listed) → "the seven screens" (+ `dom.js` is the shared builder, not a screen); e2e naming line → notes the non-story guard `test_atlas_loads.py`.
- **Decisions:** no code changes were needed; only the two doc fixes above. Docs re-verified by `test_docs_commands`/`test_docs_index` (44 passed) plus the final full gate.
- **Next pending phase:** `08_client_prediction_and_touch`.
This commit is contained in:
2026-10-10 18:03:52 -04:00
parent f26849c1f3
commit 95ca3fd1fa
78 changed files with 13650 additions and 259 deletions
+24 -1
View File
@@ -21,7 +21,7 @@ import pytest
from undercooked.config import Settings
from undercooked.server.clock import FakeClock
from undercooked.server.codes import CROCKFORD
from undercooked.server.room import Room, RoomState
from undercooked.server.room import TEST_MODE_SEED, Room, RoomState
from undercooked.server.rooms import (
GC_INTERVAL_S,
SEAT_GRACE_S,
@@ -473,3 +473,26 @@ async def test_gc_loop_runs_on_the_injected_cadence_and_is_cancellable() -> None
with pytest.raises(asyncio.CancelledError):
await next(iter(loop_task))
assert clock.now() == 3 * GC_INTERVAL_S # exactly three 5-second cycles
# ---------------------------------------------------------------- the test-mode seed
async def test_create_room_in_test_mode_uses_the_documented_fixed_seed() -> None:
"""The test-only seam: under `UNDERCOOKED_TEST_MODE=1` the room is born
with the documented fixed seed, so a `?seed=20240701` page plays the
seed the room's sim actually draws (the e2e flagship's determinism)."""
supervisor, clock, _ = make_supervisor(test_mode=True)
room = supervisor.create_room(new_session("Ada", clock), "Ada")
assert room._seed_cursor == TEST_MODE_SEED
async def test_create_room_outside_test_mode_draws_a_fresh_seed() -> None:
"""A production room never pins the seam's value: the base seed is a
fresh draw from `secrets.randbelow(_SEED_MODULUS)` (the range check —
a value equal to the constant is possible in principle, so the
assertion is the range, not an inequality)."""
supervisor, clock, _ = make_supervisor() # test_mode defaults to False
room = supervisor.create_room(new_session("Ada", clock), "Ada")
assert isinstance(room._seed_cursor, int)
assert 0 <= room._seed_cursor < 2**63