# US-02 — Join a lobby with a code **As a** friend who was told a 5-character code, **I want to** type it on my own device and be in the kitchen, **so that** we can play together from anywhere. ## Acceptance criteria 1. The landing screen has a *Join* field accepting the code, case-insensitive, tolerating whitespace and `l`/`1`-style misreads for excluded characters. 2. Joining with a valid code puts the player in a seat with a generated display name (`Cook #2`) if none was typed, and broadcasts a `lobby` update to everyone. 3. Joining a full room shows "this kitchen is full (4/4)" and the room code is kept in the field for a retry. 4. Joining an unknown code shows "no kitchen with that code" (no leak of which part failed) and never reveals whether a room exists behind a closed door. 5. Joining during `PLAYING` puts the player in `SPECTATING` (visible "watching" banner) until the next `LOBBY`. 6. A display name is sanitized (control chars stripped, 16-char cap) and is not unique-enforced; two players may share a name. 7. The second socket from the same token evicts the first cleanly (no ghost). 8. Join → seated takes < 1 s on localhost. ## E2E suite `tests/e2e/test_us02_join_by_code.py` — two browser contexts: create in A, join in B, assert both see 2 seats; full-room and bad-code paths. ## Phases `05_anonymous_lobbies_and_room_lifecycle`, `06_realtime_websocket_protocol`, `07_canvas_client_and_hud`