1. 03 Jul, 2026 29 commits
  2. 25 Jun, 2026 3 commits
  3. 23 Jun, 2026 5 commits
    • Mahmoud Aglan's avatar
      fix: draw offer race condition + add multiplayer logging · 9b2a459c
      Mahmoud Aglan authored
      Root cause: draw offers stored in game_state were lost when a
      concurrent move write did read→merge→write (non-atomic). Now uses
      PostgreSQL jsonb || operator via merge_game_state() RPC for atomic
      merges.
      
      Also adds:
      - mp_log table for server-side multiplayer event logging
      - Frontend sessionStorage logging (sessionStorage.mp_log) for
        debugging draw offers, moves, and poll events
      - Logging on match_created, move, resign, draw_accepted, game_state_merge
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      9b2a459c
    • Mahmoud Aglan's avatar
      test: add chess sync verification test with move+resign proof · 2f9d7d84
      Mahmoud Aglan authored
      Screenshots confirm: moves sync to opponent board, resign shows
      victory screen. Both directions verified via Puppeteer + API.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      2f9d7d84
    • Mahmoud Aglan's avatar
      fix: chess moves/resign silently failing — non-existent columns · b3109e7a
      Mahmoud Aglan authored
      The `matches` table has no `last_activity` or `ended_at` columns.
      PostgREST rejects the entire PATCH when ANY column doesn't exist,
      causing every move update and resignation to silently fail.
      
      - Remove `last_activity` from handleGameMove (column doesn't exist)
      - Change `ended_at` to `completed_at` in handleResign, handleDraw,
        handleComplete (correct column name per DB schema)
      
      This was the root cause of zero sync in multiplayer chess.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      b3109e7a
    • Mahmoud Aglan's avatar
      fix: bulletproof matchmaking queue across all games · 1c145579
      Mahmoud Aglan authored
      - Add dequeue handler to backgammon-match.php (was missing)
      - Add 90-second stale entry cleanup to ludo/domino/backgammon queues
      - Add atomic opponent claiming (conditional update) to prevent race conditions
      - Fix client queue.js to route dequeue to correct game endpoint
      - Register unmountQueue for proper cleanup when exiting search screen
      - Remove json_encode on jsonb fields in backgammon (prevent double-encoding)
      
      Fixes: self-matching, ghost queue entries, and race condition where two
      players simultaneously claim the same opponent.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      1c145579
    • Mahmoud Aglan's avatar
      fix: eliminate jsonb double-encoding that broke multiplayer sync · 6517bd2d
      Mahmoud Aglan authored
      The root cause: PHP's json_encode() was used on jsonb fields before
      passing to SupabaseClient->insert/update, which json_encodes the entire
      payload. This double-encoded game_state into a JSON string scalar.
      
      When the match_heartbeat RPC did `jsonb_string || jsonb_object`,
      PostgreSQL created an array instead of merging, corrupting game_state.
      The polling logic then couldn't find move_count or last_move in the
      array, so moves never synced between players.
      
      Fix: pass PHP arrays directly for all jsonb columns (game_state, moves,
      positions, scores, winners, hands, boneyard). The Supabase client
      handles encoding once at the HTTP layer.
      
      Also: when reading game_state back from the DB, handle the case where
      PostgREST returns it as a decoded array/object (not a string) since
      jsonb columns are deserialized automatically.
      
      Affected: chess, ludo, domino — all multiplayer match creation and
      move/state updates.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      6517bd2d
  4. 22 Jun, 2026 1 commit
  5. 21 Jun, 2026 2 commits
    • Mahmoud Aglan's avatar
      fix: notifications broken — select non-existent 'metadata' column · 95d81235
      Mahmoud Aglan authored
      The notifications API selected a column 'metadata' that doesn't exist
      in the table (actual column is 'data'). This caused PostgREST to
      return an error, which the error handler caught and returned empty.
      Users never saw their notifications.
      
      Also added body_ar to the select and frontend display.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      95d81235
    • Mahmoud Aglan's avatar
      fix: ensure result screens always show on win/loss in all games · 6790db43
      Mahmoud Aglan authored
      Ludo:
      - Add endingGame guard to prevent double endGame calls
      - Parse server winners from live polling (fixes empty leaderboard
        when opponent wins remotely)
      - Fix win/loss detection: last place = loss (was broken for 2-player)
      
      Chess:
      - Add 5s failsafe timer: result screen shows even if network hangs
      - Deduplicate navigation logic with navigated flag
      - Prevent double-navigation if both .then and failsafe fire
      
      Backgammon:
      - Add scene.exitGameMode() to both result navigation paths
        (was missing, could leave UI in game-mode state)
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      6790db43