Review fixes for open-game auto-match: decodeMatchResult dropped the game when matched=false (an open game awaiting an opponent), so the client never navigated into it - decode the game whenever present. The lobby grouped open games (status != 'active') into 'finished'; treat 'open' as in progress in groupGames/isMyTurn and resultBadge. The under-board status bar now reads "Opponent's turn" while the empty opponent seat is to move (instead of the searching placeholder). The New Game rule toggle is shown from the start when a Russian variant is available, so selecting a variant no longer shifts the layout.
Regression tests: codec (game decoded with matched=false), lobbysort + result (open is in progress), and the new-game e2e updated. UI-only; no backend or schema change.
- Resign on the opponent's turn: engine ResignSeat(seat) resigns a specific seat
(not just toMove); game.Resign bypasses the turn check and forfeits the actor's seat.
- Quick-match cancel was a UI no-op (only stopped polling): add the full path
(REST /lobby/cancel -> gateway lobby.cancel -> client) and clear the matchmaker's
pending result on Cancel, so a cancelled search is dequeued (no 'already queued', no
later robot-substituted game). NewGame dequeues on cancel and on abandon.
- Lobby win/loss: result.ts ranked by score, so a 0-0 resignation read as a win.
The winner now takes rank 1 and the viewer is placed from rank 2 — matching the
game-detail screen.
- Friend request to a robot: robots no longer block requests; the request stays
pending and expires (friendRequestTTL), mirroring a human who ignores it.
- Nudge cooldown: ErrNudgeTooSoon now maps to a distinct nudge_too_soon code with a
correct message; the chat nudge button disables during the hourly cooldown; the
nudge note reads 'Waiting for your move!' (button keeps the Nudge action label).
Tests: engine/service off-turn resign, matchmaker cancel-clears-result, friend-to-robot
inttest, result.ts 0-0 resignation, nudge_too_soon mapping.