1. 26 Oct, 2026 4 commits
    • Mahmoud Aglan's avatar
      fix(matchmaking): test accounts and real players never meet in a public queue · f5207cc4
      Mahmoud Aglan authored
      The play tests run against production, and a test account waiting in the
      public chess queue was seated across a real player (karimelshafei22), who
      then played an account that vanished with the test's teardown. Nothing in
      any queue knew a test account from a person.
      
      profiles.is_test (migrations/2026-10-27-test-accounts.sql, applied) marks
      an account the tests made. It is a column, not a name: usernames and
      display names are the player's own choice, so a name marker could drop a
      real player into the test pool. The profiles guard keeps the column out of
      a player's token, both ways. The migration also marks the 23 ZZZ-* test
      accounts earlier runs left behind.
      
      includes/match-pool.php keeps every search to the searcher's pool: the
      queue call and the status pickup of an earlier queuer in chess, ludo,
      domino and backgammon, and domino's public tables of three and four.
      Unknown means no: a profile that cannot be read is seated with nobody. The
      bot fill only ever holds the player's own row, after that search, so it
      needs no change. Real players among themselves pair exactly as before.
      Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
      f5207cc4
    • Mahmoud Aglan's avatar
      feat: invite codes and links, player-side orgs, guest upgrade, and the game honouring the manager · 90ab18f7
      Mahmoud Aglan authored
      Invite codes and links (one system for org, tournament, group and promo codes)
      - Redeem screen, invitation card, reveal and history; a code box at signup and
        login; links (/r/CODE, ?org=, ?code=, ?join=) survive reloads, a closed tab
        and the Google redirect; WhatsApp/Telegram previews with a shaped Arabic share
        card. Canonical codes match the SQL exactly (tests share the vectors).
      - A guest who registers is upgraded in place: same account, coins, history and
        held invites.
      - Organisations, player side only (staff manage them in the manager): browse,
        read-only org page with announcements, join, apply with a private proof,
        leave, primary org on the profile. Proofs moved to a private bucket.
      - The groups join box resolves any code through the same rate-limited path.
      
      The game honours the manager
      - Feature flags and maintenance mode, which games are on and their order,
        daily reward and Elo K from reward_config, a ban screen with the reason,
        ads on the table with counted impressions and clicks.
      - Tournaments: drafts hidden, registration window, rating limits, members
        only, entry fee charged with the seat and refunded on withdraw or cancel,
        prizes paid once at the end, announcements and the live page link, a
        paused state, withdrawn players keep their opponents' points.
      - Draws and losses are paid; the battle pass counts per season; the shop
        respects availability; the game keeps its own art (the manager cannot
        change logos, tab icons or bot portraits).
      
      Fixes
      - Realtime channels get the refreshed token and rejoin when dropped; a chat
        that failed its first load keeps trying; no vibration before the first tap;
        org and redeem notices in the bell; Dockerfile adds intl and gd.
      
      Data: Arab countries and regions dataset for tournament forms. Docs: redeem
      API, tournament hosting spec.
      Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
      90ab18f7
    • Mahmoud Aglan's avatar
      fix(chess): bot games crashed on resign and finish · 6ce8f229
      Mahmoud Aglan authored
      A top-level const in api/game.php was declared below the dispatch, so it was
      not defined yet when finishing a bot game called chessBotTooShort(): every
      bot game ended in "Undefined constant", was never closed and never paid.
      Declared above the dispatch now.
      
      Also the applied follow-up migration: account deletion no longer fails for
      code creators and reviewers, legacy group codes are unique by canonical form,
      tournament codes register through tournament_register (no drafts, window and
      fee honoured), the table keeps its game order, the test ad is paused.
      Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
      6ce8f229
    • Mahmoud Aglan's avatar
      fix(security): close coin minting and open write policies · f4eec81f
      Mahmoud Aglan authored
      Found while building the org/redeem layer; all exploitable with the public
      anon key that ships in realtime.js, or with a player's own session.
      
      - complete_match, award_coins and the match/queue functions were executable
        by anon and players: one REST call paid win coins to anyone. Service role
        only now; every caller is PHP with the service key.
      - battlepass.php credited whatever "coins" the request carried, to anyone,
        as often as asked. The server now decides the reward, needs an active
        season and an unlocked tier, and pays each tier once.
      - The legacy api/ludo.php let any player end any ludo match with any winners
        and get paid. It, api/domino.php and api/multiplayer.php were unused by the
        app and are removed.
      - Shop, challenges and daily reward wrote balances with the player's token,
        which only worked because players could PATCH their own coins over REST.
        They write with the service key now; the shop deducts only if the balance
        is unchanged (a double tap cannot pay twice), refunds if the grant fails,
        and records its transaction (the old insert used a missing column).
      - Policies: anyone could delete any ludo/domino/backgammon match, rewrite
        queues, groups and the theme, insert an "accepted" friendship with anyone
        and post into any chat. Writes are service-role only; every read and
        Realtime feed is unchanged. Group members read only their own memberships.
      - profiles-guard.sql (applied after this deploy): a player's token may change
        only cosmetic profile fields.
      
      Also the org-unify / redeem SQL layer and its contract (docs/redeem-contract.md).
      Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
      f4eec81f
  2. 25 Oct, 2026 2 commits
    • Mahmoud Aglan's avatar
      feat: player feedback round across all four games, plus sessions that survive bad lines · 0b076f00
      Mahmoud Aglan authored
      Domino
      - Public tables for 2, 3 and 4 seats, including the four-player free-for-all
        (domino_table_join/leave, domino_merge_state); bot matches pay real coins.
      - The Egyptian lock rule: a blocked game is scored, the opponent no longer
        draws the boneyard.
      
      Backgammon (tawla)
      - 31: the first runner must reach the home board before the rest may move.
      - New board colours so the white checkers read clearly (--bg-* tokens,
        DESIGN.md, branding admin).
      
      Matchmaking
      - After 60 s with no opponent, a procedurally generated player-like bot takes
        the seat (ghost_profiles); unrated, paid once, 90 s anti-farm floor.
      
      Chess and ludo
      - Premoves merged; draw on flag when the side left cannot mate; bot payout
        guard; ludo seat guard and rules fixes.
      
      Social and sessions
      - Chat: the sender on the right, the other person on the left; a thread whose
        first load failed keeps retrying instead of going silent; creating a group
        survives a dropped answer without making a second group.
      - Group chat messages readable by members only (RLS).
      - A slow auth service no longer signs players out: the server answers 503
        instead of a cached 401, refresh only ends a session the server refused,
        and realtime channels receive the refreshed token.
      - complete_match raises levels with XP.
      
      Docs: the manager/game integration audit and the org invites + events plan.
      Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
      0b076f00
    • Mahmoud Aglan's avatar
      feat(social, multiplayer): working chat and groups, reliable challenges, true... · d951ad46
      Mahmoud Aglan authored
      feat(social, multiplayer): working chat and groups, reliable challenges, true VS, resume sync, emotes from the sender
      
      Social: chat sides came from a stored user id that Google sign-in never set,
      so every message looked like the other player's; the server now marks each
      message mine and the client reads its id from the token (core/me.js). Chat
      rebuilt (my bubbles inline-end, grouping, times, seen, typing, reactions,
      quick replies), live delivery fixed, groups fully working (join by code or
      link, admins, unread, owner hand-over), presence, dock/bell/friend badges,
      friend notifications, news feed, invite links, profile actions.
      tests/play/social.mjs 106/106.
      
      Challenges: the invite was only seen on the Friends screen; a global prompt
      (play/invite-watch.js) now shows it everywhere with a countdown. Chess
      declines failed on the result enum; the handshake lives in match_invites
      with conditional updates, expiry and challenger presence. VS: self vs self
      came from the stored id and profile.php answering with the caller when the
      id was missing; every player's card now travels with the hand-off.
      Backgammon simultaneous searches and ludo table sizes fixed.
      tests/play/challenge.mjs 72/72 (4 games x 6 scenarios x 3 rounds).
      
      Resume: leaving the screen dropped the saved match; your own sleep was
      blamed on the opponent; heartbeats raced moves inside game_state (now
      match_presence); moves on a bad line were dropped (outbox) and late retries
      rolled boards back (stale steps refused); frozen loops restart on return.
      Emotes appear at the sender's plate on both screens, per seat in ludo.
      Resume and emote scenarios pass in all four suites.
      
      Migrations applied: social, match-invites, match-presence.
      Co-Authored-By: 's avatarClaude Opus 5.5 <noreply@anthropic.com>
      d951ad46
  3. 19 Oct, 2026 34 commits