Two disconnect-related money/UX gaps:
1. A confirmed single_game payment whose game never started (user paid, then
disconnected) was money-in-nothing-given with no recovery path. recover-
PendingPayments now detects confirmed single_game payments with no linked
games row and grants the equivalent credits — idempotently (per-payment
reason key, row-locked re-check of both the game link and prior recovery).
2. In-progress games could be dropped on disconnect when a fresh getBySocket
lookup failed. The stable dbUserId is now stamped on the game at creation,
so suspend/restore keys on it directly without a failure-prone disconnect-
time lookup. Any game (paid or free) is preserved/reconnectable whenever a
session exists; the remaining drop-branch is only the true no-session edge
(e.g. DB unavailable at connect), which is logged for audit rather than
masked.
Adds singleGameRecovery.test.js. Full suite: 13 suites / 119 tests.