Follow-up to #12 / PR #22.
Current behavior: after a game ends, a player who requests a rematch sees "Waiting for opponent…" indefinitely. The opponent has no way to say no — the offer only dies when they disconnect, close the sheet (Done), or queue for a new opponent (abandonRematch in GameCoordinator.swift). Ignoring the offer keeps the requester hanging until they give up on their own.
Proposal:
- Protocol: add a
decline_rematch client message; the requester receives the existing rematch_unavailable (or a dedicated rematch_declined so the UI can say "Declined" instead of "Opponent left").
- iOS: the game-over sheet's rematch row gains a decline affordance when
rematchOfferedByOpponent is set.
- Optional hardening: a server-side TTL on
RematchSlot (e.g. 60s) so abandoned slots don't linger for sockets that stay open but idle.
Acceptance: MatchFlowTests cover decline (requester notified, slot dropped, both players can still queue normally) and, if implemented, expiry. Message round-trip tests extended.
🤖 Generated with Claude Code
Follow-up to #12 / PR #22.
Current behavior: after a game ends, a player who requests a rematch sees "Waiting for opponent…" indefinitely. The opponent has no way to say no — the offer only dies when they disconnect, close the sheet (Done), or queue for a new opponent (
abandonRematchinGameCoordinator.swift). Ignoring the offer keeps the requester hanging until they give up on their own.Proposal:
decline_rematchclient message; the requester receives the existingrematch_unavailable(or a dedicatedrematch_declinedso the UI can say "Declined" instead of "Opponent left").rematchOfferedByOpponentis set.RematchSlot(e.g. 60s) so abandoned slots don't linger for sockets that stay open but idle.Acceptance: MatchFlowTests cover decline (requester notified, slot dropped, both players can still queue normally) and, if implemented, expiry. Message round-trip tests extended.
🤖 Generated with Claude Code