- 26 Oct, 2026 9 commits
-
-
Mahmoud Aglan authored
Write grants (TRUNCATE included) were still open to anon and authenticated; nothing on the client writes tournaments. Applied. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The schema as applied (read back from production), the allowed values and the conflict targets, the decisions taken where the spec left room, the JSON shapes, the SQL functions and PHP hooks each package exposes, the API contracts with one error shape and one Arabic error catalog, the manager routes with their permission keys and the org-scope guard, file ownership, the test harness, and notes for each package. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
tests/schema.sql is now generated from read-only dumps of production: the 35 tables the tournament system touches, with their checks, keys, indexes, RLS, policies and grants, and the production functions the hosting packages build on (tournament_register, _withdraw, _refund_entry, _refund_due, _settle, _redeem_tournament, redeem_fold, complete_match, award_coins). It used to be a hand-written subset without any of those functions. tests/run.sh loads it strictly, applies every 2026-10-27-hosting-*.sql (the schema file first), proves a second run leaves the schema identical and that the hosting tables stay locked to the service role, then runs every tests/test_hosting_*.php after the existing suites. PgShim now behaves like PostgREST where the engine relies on it: rpc() runs the real SQL function with named arguments and the client's return shapes, upsert() exists, jsonb, arrays and numerics come back typed, and a PHP list written to an array column becomes a Postgres array. The test router's supabaseRpc() goes through it. The suite was red before this (3 passed, 2 failed): registration and settlement are SQL functions now, and the shim answered every rpc with []. The end-to-end test also crashed at section 9, so sections 9 to 21 never ran; its heartbeat checks still looked in game_state, while heartbeats moved to match_presence. Now: 8 passed, 0 failed. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
One idempotent migration for every hosting package: stages carry their own rounds, teams, team matches and stage entries, playable brackets, check-in, waitlist, registration forms and answers, prize categories and a prize hold, presentation (media, sponsors, ad slots, announcements), disputes, certificates, notices, forbidden pairs, tournament columns on the backgammon, domino and ludo tables, and club and lobby groups. Two storage buckets: tournament-media (public) and tournament-uploads (private). The 21 tournament tables keep RLS on with the service role only: anon and authenticated hold no grant at all (they read nothing there today; RLS had no policy for them). el3ab_tournaments and el3ab_tournament_rounds keep the read policies Realtime needs. Applied to production on 2026-10-26 after a backup in /root/backups/2026-10-27-hosting-schema/, then run a second time: the schema dumps before and after the second run are identical, and the 9 tournaments, 16 registrations and 39 rounds are unchanged (the 4 legacy string pairings hold objects, so none was unwrapped). A commented rollback block is at the end of the file, tested inside BEGIN/ROLLBACK on production. Departures from the spec, each recorded in docs/tournament-hosting-contract.md: 'unpaired' rounds stay legal only for the old Swiss service, a team event must have a team size, dispute_window_minutes gets a column, groups get kind and tournament_id. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Every account helper in tests/play sets profiles.is_test right after the account is made (guest or GoTrue admin), before it can queue, and fails loudly when the marker did not stick. tests/play/match-pool.mjs: two test accounts and two unmarked stand-ins for real players, made with the admin API (no guest signups). In every public queue, in a bucket the app never offers a real player where the game has one, a test account is not seated with a real player waiting longer, by the queue call or the status poll; two test accounts are seated together on both paths; a real player searching passes a waiting test account; two real players still pair; domino's public tables keep apart the same way; and a player's own token cannot set is_test either way. Against the code before the fix it fails 42 checks; after, 63 of 63 pass. Teardown is verified. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
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:Claude Opus 5.5 <noreply@anthropic.com>
-
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:Claude Opus 5.5 <noreply@anthropic.com>
-
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:Claude Opus 5.5 <noreply@anthropic.com>
-
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:Claude Opus 5.5 <noreply@anthropic.com>
-
- 25 Oct, 2026 2 commits
-
-
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:Claude Opus 5.5 <noreply@anthropic.com>
-
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:Claude Opus 5.5 <noreply@anthropic.com>
-
- 19 Oct, 2026 29 commits
-
-
Mahmoud Aglan authored
Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
A bot game gets its own ludo_matches row (action start, mode bot), and complete pays its creator once through complete_match with the bot seats as null: nothing under 90 s, nothing for a walked-away game, 403 for anyone else. The result card shows the real coins and XP. From the design review: the die is the roll key (#roll-btn), an idle full face on a lip in the roller's colour, instead of a die beside a button with a red pip in a glowing ring; plates use the shared avatar (purple rounded square for people, ink with the robot for bots); the room's start key is pinned above the dock with compact seat cards, checked uncovered at 360x640, 390x844 and 430x932. tests/play/ludo.mjs 83/84 then 37/37 for the local games after fixing the opening roll in the one lucky check. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
DESIGN.md (and .impeccable/design.json) replace the June v3 spec with the shipped Night Arcade system, derived from tokens.css and core.css: the stage, ink tiles, white cards, keys on a lip, colour rationing, the board palettes, Rubik with the pinned display face, motion and feel, RTL, and the theming contract. The documenter's audit found the code breaking its own rules: a white rim on top of every key and the play key read as gloss (removed), the green and orange faces had a lighter top that dropped white text under 3:1 (both are one flat colour now, orange deepened to 3.4:1), and small green keys carried 13-14px text at 3.1:1 (they use a deeper green, 5:1). The CTA glint stays, recorded as motion. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The backgammon game never entered game mode, so the HUD and the dock stayed over the board (screenshots only hid it when an earlier game had left game mode on). It now enters on mount and exits on unmount like the other games. Avatars follow one rule across games: a person is a purple rounded square on a lip, a bot an ink rounded square with the robot (chess's player tile was blue, backgammon's were circles). backgammon.mjs --only result,layout: 27/27. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
From the finish review: - Contrast: white on the green face was 2.0:1; the green (and orange) button faces are deepened so white text clears 3:1. Green stickers on a green key render white instead of vanishing. The shop's common and legendary headers print in ink, on tokens. - HUD: the purses keep their plus keys on phones; the avatar with its level badge stands for the player and the name moves to the profile. - Domino: the table was purple-tinted (purple flooded the screen); it is plain ink, purple stays on the tile backs. - Home: the game cards overlap less so every name shows; the duplicate leaderboard entries (a side key and a chip, besides the dock tab) give way to a shop key; a live tournament is an ink tile with a red live bead, not a red slab. - Leaderboard rows stay inside the gutter; the player's own row is lit. - index.php FORM records that this world had no concept roll or comp. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
award_coins answers the new balance; json_decode gave an int and SupabaseClient::request(), typed to return an array, threw a TypeError. Every achievement that paid coins answered 500 after the coins were already granted (seen in production on achievements.php). Scalars now come back as ['result' => value]; error bodies that are not objects no longer index into a scalar. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Nothing paints wood any more; the texture, its credit and the legacy --wood-* tokens go. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Rules: move generation is rewritten so both dice must be played when they can be, otherwise the larger. 31 used a long-backgammon setup and allowed pins; it now starts like mahbousa, a single checker closes a point and the winner scores the loser's remaining checkers (lengths 1/11/21/31). Mahbousa could freeze forever when both starting stacks were pinned; the plakoto mother rule now settles it. Turning the cube off lasted one game, taking the bot's double froze the game, and a player on black saw the board upside down with a swapped score. Online: a poll could wipe unsent moves, the cube never synced, 'opponent' was sent as the winner id, and a refreshed tab rejoined as white. Online is now a numbered move log plus a snapshot; the other side replays the log with animation and checks it matches. Timers stop when the scene closes. Bot games get a server match row (action start) and complete pays the creator once; a row under 90 s old closes with no payout. Look: flat teal board, red and white points, checkers on a lip, flat dice and cube. backgammon-rules 659/659 (brute-force cross-check); browser suite 122 + online 39/39 + layout 20/20, zero console errors. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Accounts named ZZZ-... are test accounts (every script in tests/ and tools/ tags them so); the leaderboard skips them in every game instead of showing eight ZZZ-CYCLE rows at the top. shoot.mjs waits for requests the closing page had in flight and repeats its cleanup until the profile is gone: a bot match created at the last moment had kept one alive. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
p004 (tagged mate) let the defender take the rook; the black rook is gone so Ra8 is mate. p009 was tagged promotion with a rook move; it is a back- rank mate. p019 (tagged mate) left the queen hanging; it is now Qh8 mate with the king guarding. Rows before the change are in this file; all three answers were checked as mate with chess.js. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Achievement coins were paid by the check after the HUD had already pulled the player, so the purse showed the old balance until the next refresh. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The form posted every field, pre-filled with the Cafe Deck defaults, and the save stored each one in platform_theme, where stored values beat tokens.css: one save would have turned the app indigo again. It now sends only the fields that changed, and its defaults are the Night Arcade values. Pawn uploads go to the ludo_token_* slots the board now reads. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The uploaded piece images were heavily outlined old-style art; they are backed up to docs/branding-backup-2026-10-19-chess-pieces.json and cleared in platform_assets, so every board draws the flat Night Arcade pieces. White pieces get a thin mid-grey edge and a silver lip to read on the light squares. tools/shoot.mjs --drop-assets previews the built-in art. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Online: at speed a client's writes trail its own board, and a stale-row check read that lag as a stale row, reset the client and let it play again from the older board: two writers with one sequence number. The check now fires only with nothing in flight, every write is dropped while a resync is pending, and a resync takes the row's sequence. The quiet-player notice shows for each new silence, not once per match. Rules: in 3-4 player games the first finisher kept a turn nobody could take and the game stalled; three sixes is enforced and no longer carries into the next turn. Seats come from the match, not the room. Exactly one client settles a match, with real ids. Bots score their moves by difficulty. A leaver is announced and in a duel the one who stays wins. Names in the roll button are escaped. Look: flat quadrants on white, pawns on a lip, a flat tumbling die. tests/play/ludo.mjs 73/73 (local 4, duel, online), ludo-rules 60/60. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Live games lost their move history (each opponent move arrived as a bare FEN), so threefold repetition never triggered and the PGN broke; moves now replay from the server's list. The mating move was sent after the game closed and never reached the opponent. A refreshed tab overwrote the server's move list with a short one; it now rebuilds from the server. The opponent got the increment twice, Black's clock started before the first move, and a flag is now confirmed with the server. Refreshing a bot game lost it; the bot accepted half of all draw offers at random and froze on an illegal reply. Promotion has a cancellable picker. The result card no longer invents rating changes for bot games, rematch after a live game searches at the same clock, and back to table no longer stacks a home. Analysis kept the evaluation sign and orientation for Black; review runs one engine call per position and caches. Puzzles accept an alternative mate and offer retry / show answer. Board: flat lavender in an ink frame, pieces on a lip, tokens only. tests/play/chess.mjs: 183/183 (bot, online with two accounts, puzzles, three phone sizes), zero console errors. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Shell (match UI, player plates, dialogs, reactions, queue, VS, challenge): flat Night Arcade surfaces, turn timers on the plates, banners for your turn / time low / waiting, a claimable disconnect ring, and a cancel that waits for an in-flight join so it never leaves a ghost queue row. tests/play/shell.mjs: 144/147 (the 3 were the invite bugs below). Domino: flat white tiles on a lip, a purple-tinted table, playable tiles lifted, the hand fanned with translate. tests/play/domino.mjs: 785/785 against the bot, scripted positions and online. friends.php: a ludo invite wrote a column ludo_matches does not have (400) and opened a four-seat table; domino and ludo invites stored players as a JSON string, so check-invites never showed them to the friend. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
isMatchParticipant() knew only ludo and domino matches, so every reaction poll in an online backgammon game answered 403 and nothing reached the opponent. Backgammon seats come from players or the colour columns. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
On narrow phones the purse keeps its tap but drops its plus key and the name tightens, so the bell no longer falls off the screen edge. The eight FIDE title labels on the profile editor were missing. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The owner rejected the walnut and leather look as dated. The new world: a quiet violet-black ink stage with a faint star lattice; flat ink tiles for lists (each row its own tile); white game cards on a lip; flat colour buttons on a solid lip the finger pushes down; a floating HUD and a floating dock with the green play key standing out of it; Rubik, self- hosted, with the display face pinned to ExtraBold. Stickers are rebuilt flat on a lip, the game art is redrawn flat, each game owns one hue. Home, HUD, rewards, auth, shop, settings, profile and the leaderboard are converted; game screens follow. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Chess: a status poll that landed between the claim and the match id write deleted the claim, so the waiting player searched forever while the other sat alone. A claim in progress is now left alone for 3 minutes, dequeue reports it, a failed match insert releases it, and a waiting player's status poll claims an older waiting row. Domino gets the same status-side match. Raced 4/4 (chess) and 3/3 (domino) with simultaneous queue calls. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
justify-content centred the register form past the top of the screen; auto margins centre it without overflowing. The age checkbox is a carved slot that pops a green tick, and checkboxes squash and click like buttons. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Each challenge shows its game's sticker (ludo_played, chess_played...) and a ready one glows gold until collected. The all-done panel was never shown; it now appears once every challenge is claimed. The API's fallback icons and the stored templates use sticker names instead of emoji. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Both missed each other in the queue call and both inserted; status never tried to match, so they waited for the 60 s timeout. A waiting player's status poll now claims fresh rows queued before its own, after locking its own row, so two pollers can never claim each other. ludo_queue stores the requested table size for that path. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
decline-invite only accepted the invited player, so a host backing out of the lobby got 403 and the friend could still accept into an empty game for two minutes. The host now cancels (result 'cancelled'); the target still declines. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Bot game scenes create domino/ludo/backgammon match rows and, now that payouts work, economy and rating rows; removeAccount left them behind. Twelve orphaned domino matches from earlier runs were deleted from production. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Haptics (feel.haptic) are off when the player turns vibration off; the setting persists with the rest of the player's preferences. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
bounce-easing is the owner's direction (El3ab is a game: pops and overshoot); side-tab flags the 3D bevel under buttons and cards. The XP bar used to transition width (layout thrash); it now scales. Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Co-Authored-By:Claude Opus 5.5 <noreply@anthropic.com>
-