Repository navigation
feat(core): gaming, sportsbook and bonus read tools for the agent kernel - #406
mp-blurify wants to merge 6 commits into
Conversation
…ools The analytics read tools resolved and bounded their whole UTC days with helpers private to analytics. The reporting contract now holds them - resolveDayRange, checkDayRange and dayRangeInstants - so every module's report tool takes the same dateFrom and dateTo, defaults the same way and covers the same instants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Mateusz Paś <mateusz.pas@studiosoftware.com>
gaming.top_games, under report:view, ranks the games by their completed rounds over 1 to 92 whole UTC days, separately per currency: volume, revenue, rounds played and distinct players, as the back office game performance report counts them, with each currency's totals over every game played. sortBy and sortDir pick the figure and the order, limit takes 1 to 50 games, gameType and currency narrow it, and at most 20 currencies are listed. It names no player. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Mateusz Paś <mateusz.pas@studiosoftware.com>
sportsbook.turnover_exposure, under sportsbook:view, totals every player's bets per currency over 1 to 92 whole UTC days by the player summary's rules, now one shared query: stakes of the bets accepted or still reserved in range, payouts of those settled in it, and GGR. It adds the open bets now - their count, stakes and potential payout - and the 0 to 20 largest open bets by potential payout, each by player id with its stake, payout, number of legs and when its last leg starts, never its selections. The tool builds its reader from DRIZZLE alone, since the kernel builds every handler when it starts. A partial index on settled_at serves the settlement side, which no longer reads one player's bets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Mateusz Paś <mateusz.pas@studiosoftware.com>
bonus.grants_summary, under bonus:view, reports per currency over 1 to 366 whole UTC days: the grants funded in range and the bonus they credited; the grants that ended in it - converted with the bonus paid out as cash, expired and forfeited with the balance they took away and the forfeit reasons, cancelled - split by source; and what the live grants hold now: their bonus balance, the wagering still required and those expiring within 7 days. It names no player and returns no note. Partial indexes on activated_at and closed_at serve the range reads; the closing ledger entries are read by grant id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Mateusz Paś <mateusz.pas@studiosoftware.com>
…to end through the mcp kernel The kernel suite expects the three tools under their owners and calls each against rows seeded in a currency of its own: exactly the allow-listed keys, the game ranked with its figures, the open bet by player id without its event, the grant and the liability it holds, no user id in what is served, invalid_input for a range out of bounds, and forbidden - audited once - for an admin whose role lacks the tool's grant. The transport suite lists them under their model names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Mateusz Paś <mateusz.pas@studiosoftware.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Mateusz Paś <mateusz.pas@studiosoftware.com>
| * game performance report reads (ADMIN_GAME_REPORTING): stakes net of reversed stakes, wins, and | ||
| * who played. | ||
| */ | ||
| export class TopGamesService { |
There was a problem hiding this comment.
Do we need a separate service for the agent? Can't we reuse listGamePerformance? It already has these figures and the sort; a per-currency method on ADMIN_GAME_REPORTING (like rankAllGamesByRounds) would also keep the tool in line when an operator rebinds the port.
| 'stands now and as the per-player sportsbook summary counts them: bets and stake (cash and ' + | ||
| 'promotion-funded) of the bets accepted, or still reserved, in range; payout (cash and ' + | ||
| 'promotion-funded, a void returning its stake included) of the bets settled in range; and ' + | ||
| 'GGR, cash stake minus payout. The open figures are now, whatever the range: openBets, ' + |
There was a problem hiding this comment.
This ggr comes out different from financial.summary's sportsbook GGR for the same range, for example after a reopen. Drop it and point to financial.summary, or say in the description that it's a different figure.
| .select({ | ||
| ...totals.columns, | ||
| openBets: sql<number>`count(*) filter (where ${openBet})`.mapWith(Number), | ||
| openPotentialPayout: sql<string>`coalesce(sum(${bet.potentialPayout}) filter (where ${openBet}), 0)::numeric(38, 18)`, |
There was a problem hiding this comment.
This is sumOf from bet-query.service.ts:103 inlined, and grants-summary and top-games each declare their own too. Can we export one money-sum helper and use it in all four places?
Summary
gaming.top_games(gaming,report:view): the games ranked per currency by their completed rounds over a range - volume, revenue, rounds and distinct players - as the back office game performance report counts them, with each currency's totals over every game played.sortByandsortDirpick the figure and the order, so the biggest losers are one call away.sportsbook.turnover_exposure(betting,sportsbook:view): operator-wide stakes, payouts and GGR per currency over a range, by the per-player sportsbook summary's rules, plus the open bets now - their count, stakes and potential payout - and the largest open bets by player id.bonus.grants_summary(bonus,bonus:view): per currency, the grants funded in a range, how grants ended in it (converted with the bonus paid out as cash, expired, forfeited with its reason, cancelled), split by source, and what the live grants hold now - bonus balance, wagering still required and what expires within 7 days.sportsbook_bet.settled_at(settled bets) and onpromo_grant.activated_atandclosed_at, which the operator-wide reads range over.Why
An operator's assistant answers questions about the operation from the read tools an admin holds. "Which games drive the volume this month", "what does the sportsbook stand to pay out on open bets" and "how much bonus did we hand out, and how much of it is still a liability" had no tool: the casino, sportsbook and bonus modules served nothing to the kernel.
Each tool reports per currency and never sums across currencies, bounds its range (92 days for games and sportsbook, 366 for bonuses) and caps every list (20 currencies; 50 games, 20 open bets). Only the sportsbook tool names a player, by player id under
playerId; none returns a bet's selections, events or markets, a forfeit note or an admin.Alternatives considered
ADMIN_GAME_REPORTING.listGamePerformance: without a currency it sums amounts across currencies unconverted, and it lists every game in the catalogue. The tool ranks per currency in one query over the same rounds, so its figures match the report's.bonus.grants_summaryfrom the bonus ledger alone: a ledger range read has no index by entry type, and a forfeit entry does not say whether it closed a grant. The tool reads the grants funded and ended in the range by their own timestamps, and takes from the ledger only the closing entry of each ended grant, by grant id.DRIZZLE.Risks
sportsbook_bet_settled_at_idxand the twopromo_grantones are built withoutCONCURRENTLY, as every generated migration is, so a large table locks writes while they build.sportsbook.turnover_exposure's stake side still reads every bet accepted, or reserved, in the range: on a busy book a 92-day range is a large read, served by the existingaccepted_atandstatus, created_atindexes.bonus.grants_summarycounts a grant's conversion by its closingconvertentry; a conversion cap's surplus is a separateforfeitentry and is not inconvertedorforfeited.