phase: 06_realtime_websocket_protocol

All green. Final verification complete — writing the report.

**Final pass report — phase 06 `realtime_websocket_protocol`**

- Verified: all 6 task files present in `complete/`; this was the final verification pass (no implementation work outstanding)
- `uv run ruff check .` → all checks passed; `uv run pyright` → 0 errors
- `uv run undercooked-levels validate` → content OK; `uv run pytest` (full suite) → **1751 passed, 0 failed, coverage 97.30% (>90% floor)**; slow sim stage (`-m slow tests/sim --no-cov`) → 2 passed; repo hygiene clean
- App launches: `/healthz` 200, `/` 200, `/api/levels`, `/api/config`, `POST /api/rooms` (201), `/api/stats` all verified live on an ephemeral port (server cleaned up)
- Criteria: scripted client room→hello→ready→start→countdown→≥20 snaps→round — PASS (`test_ws_round_two_clients.py`); identical `round` payloads both clients — PASS (`round_a == round_b`); version mismatch → `err protocol_mismatch` + close `4400` — PASS; 64 KB refused / 4 KB boundary, 500-burst → `rate_limited` → `4404` with bounded queue, 2000 malformed + 200 handshakes within memory bound — PASS; snap ≤ 1.5 KB (4p/4o/3h) and 64×64 ≤ 4 KB, downlink ≤ 60 KB/s over 100 ticks — PASS; `docs/PROTOCOL.md` model-sync test-enforced in both directions + full-round sequence diagram present — PASS
- Docs: README.md had 3 stale spots — status section (claimed 01–05 complete, 06 pending → now 01–06 complete, 07 pending), "until phases 06/07 wire the transport" wording, and the `PROTOCOL.md` layout note ("phase 06 completes it"). All fixed. AGENTS.md verified consistent — no changes needed
- No defects found in completed phases; no live infrastructure touched

Next pending phase: `07_canvas_client_and_hud`.
This commit is contained in:
2026-10-09 11:13:25 -04:00
parent f51ba919af
commit f26849c1f3
44 changed files with 11525 additions and 72 deletions
@@ -0,0 +1,55 @@
# Phase 06 — Realtime WebSocket protocol
**Story:** `US-02_join_by_code`, `US-03_ready_up_and_start`, `US-05_orders_score_and_result` (delivery), `US-09_disconnect_and_rejoin` (partial)
**Context:** `.agents/PLAN.md` §7 (protocol), §4 (data flow, snapshot), A3, A4, A14
## Objective
Put the tested room core on the wire: `/ws/{code}` with a versioned JSON protocol, strict
inbound validation (shape, size, rate, per-state legality), input ingestion with
sequence numbers, and snapshot/event broadcasting at the configured rate — so two browsers
can already play a round. This is the last phase before the game becomes visible.
## Dependencies
- `05_anonymous_lobbies_and_room_lifecycle` (complete) — supervisor, `Room`, lobby
payloads, session store
- `04_kitchen_pipeline_and_scoring` (complete) — sim state and result payloads to encode
## Tasks
1. `01_protocol_models_and_wire_contract.md` — `server/protocol.py`, message models, `docs/PROTOCOL.md`, protocol version
2. `02_ws_endpoint_and_handshake.md` — `/ws/{code}`, origin/token checks, caps, error codes, single-socket-per-token
3. `03_input_ingestion_and_validation.md` — `input` handling, seq discipline, clamping, per-state whitelist
4. `04_snapshot_and_event_encoding.md` — `server/encoding.py`, quantized snapshots, event batching, `SNAPSHOT_HZ` pacing, size budget
5. `05_heartbeat_disconnect_and_seat_grace.md` — heartbeats, seat grace on drop, ghost parking, rejoin within the window
6. `06_websocket_integration_suite.md` — two-socket full round, malformed flood, rate limit, size cap, latency injection flag
## Testing & Quality
- Unit: every protocol model's accept/reject surface; encoder quantization and size;
whitelist per room state; seq stale/rollback rules.
- Integration (real WebSockets via `TestClient`/`websockets` against a live uvicorn on an
ephemeral port): handshake, welcome shape, `ready`→`start`→`countdown`→`snap` stream,
two clients both reach the same `round` payload, unknown message types, oversized frame,
message flood, wrong protocol version close code `4400`, second socket evicts first,
abrupt close keeps the seat for the grace window and a reconnect resumes snapshots.
- Budget tests: encoded snapshot ≤ 1.5 KB for a 4-player level-1 state; downlink ≤ 60 KB/s
per player over a measured 5 s window.
- Coverage: **>90%** on new/modified code; `server/protocol.py`, `server/encoding.py` ≥ 95%.
- The phase is "Complete" only when the **entire** project test suite passes 100%.
## Documentation & Consistency
- The final pass reviews `README.md` and `AGENTS.md` in full against the project as it
stands after this phase — fixing every inconsistency found, **including pre-existing
ones not caused by this phase**.
- `docs/PROTOCOL.md` is the deliverable contract: message tables, close codes, error codes,
size/rate limits, versioning policy, and a sequence diagram of a full round. Every field
in code must appear in the doc (a test compares model field sets to the doc tables).
## Completion Criteria
- [ ] a Python `websockets` client can: create room over HTTP → connect `/ws/{code}` → `hello` → `ready` → host `start` → receive `countdown` then ≥ 20 `snap` frames → drive a scripted input to `round`
- [ ] two clients in one room receive identical `round` payloads
- [ ] protocol version mismatch closes with code `4400` and a clean client message
- [ ] oversized (> `max_message_bytes`) and flood (> `max_inputs_per_s`) cases are refused with the documented error/close codes and no memory growth
- [ ] snapshot ≤ 1.5 KB and downlink ≤ 60 KB/s measured by tests
- [ ] `docs/PROTOCOL.md` matches the implemented models (test-enforced) and includes the round sequence diagram
- [ ] full test suite **100% passing**, coverage >90%
- [ ] README.md and AGENTS.md are accurate and consistent with the whole project (verified in the final pass)
- [ ] no behavior change in completed phases