2025-10-10 23:04:50 -07:00
|
|
|
{
|
|
|
|
|
"version": "7",
|
|
|
|
|
"dialect": "postgresql",
|
|
|
|
|
"entries": [
|
|
|
|
|
{
|
|
|
|
|
"idx": 0,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1732076135211,
|
|
|
|
|
"tag": "0000_short_donald_blake",
|
|
|
|
|
"breakpoints": true
|
2025-10-11 00:07:39 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 1,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760165302772,
|
|
|
|
|
"tag": "0001_serious_la_nuit",
|
|
|
|
|
"breakpoints": true
|
2025-10-11 00:29:04 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 2,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760166775427,
|
|
|
|
|
"tag": "0002_tired_iron_man",
|
|
|
|
|
"breakpoints": true
|
2025-10-11 00:53:39 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 3,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760167848365,
|
|
|
|
|
"tag": "0003_public_shadowcat",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 4,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760168383812,
|
|
|
|
|
"tag": "0004_spicy_zarda",
|
|
|
|
|
"breakpoints": true
|
2025-10-11 01:03:31 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 5,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760169537260,
|
|
|
|
|
"tag": "0005_abnormal_thunderbird",
|
|
|
|
|
"breakpoints": true
|
2025-10-11 21:10:01 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 6,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760241924414,
|
|
|
|
|
"tag": "0006_needy_junta",
|
|
|
|
|
"breakpoints": true
|
2025-10-12 21:16:00 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 7,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760327817376,
|
|
|
|
|
"tag": "0007_pink_nebula",
|
|
|
|
|
"breakpoints": true
|
2025-10-13 10:30:47 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 8,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760375514773,
|
|
|
|
|
"tag": "0008_salty_trauma",
|
|
|
|
|
"breakpoints": true
|
2025-10-13 15:00:59 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 9,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760392647508,
|
|
|
|
|
"tag": "0009_fuzzy_korvac",
|
|
|
|
|
"breakpoints": true
|
2025-10-14 12:20:36 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 10,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760469105799,
|
|
|
|
|
"tag": "0010_complex_the_stranger",
|
|
|
|
|
"breakpoints": true
|
2025-10-14 21:20:58 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 11,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760501621605,
|
|
|
|
|
"tag": "0011_gifted_mystique",
|
|
|
|
|
"breakpoints": true
|
2025-10-14 22:04:37 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 12,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760504208398,
|
|
|
|
|
"tag": "0012_yielding_phil_sheldon",
|
|
|
|
|
"breakpoints": true
|
2025-10-15 08:58:35 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 13,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760543654915,
|
|
|
|
|
"tag": "0013_pink_the_order",
|
|
|
|
|
"breakpoints": true
|
2025-10-15 21:21:33 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 14,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760588333462,
|
|
|
|
|
"tag": "0014_lowly_gateway",
|
|
|
|
|
"breakpoints": true
|
2025-10-15 21:50:02 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 15,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760589195551,
|
|
|
|
|
"tag": "0015_exotic_loners",
|
|
|
|
|
"breakpoints": true
|
2025-10-15 22:02:21 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 16,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760590495868,
|
|
|
|
|
"tag": "0016_majestic_roulette",
|
|
|
|
|
"breakpoints": true
|
2025-10-16 00:32:48 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 17,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760599350154,
|
|
|
|
|
"tag": "0017_calm_zarda",
|
|
|
|
|
"breakpoints": true
|
2025-10-20 15:03:11 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 18,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1760996053371,
|
|
|
|
|
"tag": "0018_numerous_stardust",
|
|
|
|
|
"breakpoints": true
|
2025-10-21 23:22:17 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 19,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1761112236540,
|
|
|
|
|
"tag": "0019_acoustic_ben_grimm",
|
|
|
|
|
"breakpoints": true
|
2025-10-28 23:39:34 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 20,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1761719248881,
|
|
|
|
|
"tag": "0020_slim_spacker_dave",
|
|
|
|
|
"breakpoints": true
|
feat: Implement bracket expansion plan to support various tournament structures
- Added a comprehensive plan for bracket expansion, including support for 4, 8, 16, 32, and 68 team formats.
- Introduced a template-based bracket system with predefined templates for NCAA March Madness, NFL Playoffs, NBA Playoffs, and simple brackets.
- Updated UI flow for bracket creation, allowing admins to select templates and assign participants flexibly.
- Enhanced database schema to accommodate new scoring rules and bracket templates.
- Proposed updates to scoring logic to handle non-scoring rounds and participant placements correctly.
- Documented implementation phases for gradual rollout of new features.
- Addressed critical bugs in the playoff event processing and scoring logic, ensuring proper advancement and scoring rules across multiple leagues.
2025-11-03 09:36:16 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 21,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1762190473418,
|
|
|
|
|
"tag": "0021_fair_ser_duncan",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 22,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1762191156317,
|
|
|
|
|
"tag": "0022_shiny_mother_askani",
|
|
|
|
|
"breakpoints": true
|
2025-11-03 13:16:37 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 23,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1762200550705,
|
|
|
|
|
"tag": "0023_cynical_jack_power",
|
|
|
|
|
"breakpoints": true
|
2025-11-17 22:19:46 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 24,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763276901482,
|
|
|
|
|
"tag": "0024_faithful_mesmero",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 25,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763277000000,
|
|
|
|
|
"tag": "0025_rename_participant_ev_season_to_sports_season",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 26,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763278000000,
|
|
|
|
|
"tag": "0026_add_source_odds_to_participant_ev",
|
|
|
|
|
"breakpoints": true
|
2026-02-14 22:30:12 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 27,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763279000000,
|
|
|
|
|
"tag": "0027_add_projected_points_to_team_standings",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 28,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763280000000,
|
|
|
|
|
"tag": "0028_add_tournament_groups",
|
|
|
|
|
"breakpoints": true
|
2026-02-19 11:26:40 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 29,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763281000000,
|
|
|
|
|
"tag": "0029_change_participant_ev_to_decimal",
|
|
|
|
|
"breakpoints": true
|
2026-02-23 23:23:24 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 30,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763282000000,
|
|
|
|
|
"tag": "0030_add_draft_picks_unique_slot",
|
|
|
|
|
"breakpoints": true
|
Claude/redesign autodraft queue c4 kp r (#40)
* Redesign autodraft queue system with three-state control and queue-only constraint
Core Logic & Database:
- Add `queue_only` boolean column to `autodraft_settings` (migration 0031)
- Rename autodraft UI states: Off / Next Pick / All Picks (while_on mode maps to All Picks)
- `autoPickForTeam`: respects new `queueOnly` param — skips EV fallback when enabled
- `executeAutoPick`: auto-disables autodraft + emits socket event when queue empties with queueOnly ON (AC3)
- `autodraft-updated` socket event now includes `queueOnly` field
Mobile UI Overhaul:
- Rename "Lobby" tab → "Available" (AC6)
- Add new "Queue" tab to mobile bottom nav with drag-reorder, per-item Draft buttons, and autodraft controls (AC5)
- Controls tab retains commissioner tools, notifications, exit; queue controls moved to Queue tab
- Turn indicator appears on both Available and Queue tabs
Components:
- `AutodraftSettings`: replaces toggle+radio with three-state button group (Off | Next Pick | All Picks) + "Only autodraft from queue" switch (AC1, AC2)
- `QueueSection`: adds `canPick` prop + per-item Draft buttons for instant drafting when on the clock
Desktop (AC4):
- Sidebar QueueSection unchanged in position; gains same three-state controls and Draft buttons
Tests (AC7):
- `autodraft.test.ts`: updated for queueOnly field and socket event shape
- `timer-autodraft.test.ts`: new tests for queue-only constraint, auto-shutoff transitions, and all three autodraft states
https://claude.ai/code/session_01PYhJicAStoJ2u6q6dV1naB
* fix: remove erroneous ?? fallbacks in autodraft socket emissions and add autoPickForTeam tests
- Remove `?? true` default on queueOnly in queue-empty auto-disable socket emit
(line 488) — was dead code since the column is NOT NULL, but semantically wrong
and would have caused client-side UI desync if the type ever relaxed
- Remove `?? false` default on the next_pick auto-disable path for consistency
- Add app/models/__tests__/auto-pick.test.ts with 6 tests covering the queueOnly
constraint: empty queue, all items drafted, partial queue skip, and EV fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add missing queueOnly prop to AutodraftSettings test fixtures
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: rewrite AutodraftSettings tests for three-state button group UI
The component was redesigned from a switch + radio buttons to Off/Next Pick/All Picks
buttons with a separate queue-only Switch toggle. Updated 17 stale tests and added 5
new tests covering the queue-only toggle and the All Picks/Off button interactions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-27 22:16:26 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 31,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763283000000,
|
|
|
|
|
"tag": "0031_add_autodraft_queue_only",
|
|
|
|
|
"breakpoints": true
|
2026-03-07 21:59:29 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 32,
|
|
|
|
|
"version": "7",
|
2026-03-09 15:34:31 -07:00
|
|
|
"when": 1763284000000,
|
2026-03-07 21:59:29 -08:00
|
|
|
"tag": "0032_consolidate_playoff_pattern",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 33,
|
|
|
|
|
"version": "7",
|
2026-03-09 15:34:31 -07:00
|
|
|
"when": 1763285000000,
|
2026-03-07 21:59:29 -08:00
|
|
|
"tag": "0033_add_schedule_event_type",
|
|
|
|
|
"breakpoints": true
|
2026-03-09 15:34:31 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 34,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763286000000,
|
|
|
|
|
"tag": "0034_add_ev_snapshots",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 35,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763287000000,
|
|
|
|
|
"tag": "0035_increase_prob_precision",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 36,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763288000000,
|
|
|
|
|
"tag": "0036_increase_ev_precision",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 37,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1763289000000,
|
|
|
|
|
"tag": "0037_add_simulator_type_to_sports",
|
|
|
|
|
"breakpoints": true
|
2026-03-10 10:27:58 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 38,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773122866361,
|
|
|
|
|
"tag": "0038_sad_wilson_fisk",
|
|
|
|
|
"breakpoints": true
|
2026-03-10 23:04:51 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 39,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773200000000,
|
|
|
|
|
"tag": "0039_add_ucl_bracket_simulator_type",
|
|
|
|
|
"breakpoints": true
|
Add playoff match game scheduling and odds management (#135)
* Add playoff match games and odds storage
Introduces two new tables for bracket matchup detail storage:
- `playoff_match_games`: tracks individual game schedules within a
series matchup (game number, scheduledAt, status, per-game scores,
winner). Supports scheduled/complete/postponed status enum.
- `playoff_match_odds`: stores moneyline odds per participant per
matchup (single upsert record, no isLatest complexity).
Includes:
- Drizzle schema + relations with CASCADE deletes from playoff_matches
- Migration 0040_fat_puma.sql
- playoff-match-game.ts model with pure helpers: computeSeriesScore,
isSeriesComplete, getSeriesLeader — plus full CRUD
- playoff-match-odds.ts model with pure helpers: americanToImpliedProbability,
impliedProbabilityToAmerican, normalizeOdds — plus upsert/read/delete
- findPlayoffMatchesByEventId and findPlayoffMatchById updated to
include games and odds in their query results
- Bracket server route: add-game, update-game, delete-game,
upsert-odds, delete-odds actions
- Bracket admin UI: expandable per-match panel for game schedule
management and moneyline odds entry
- 41 new unit tests (18 game + 23 odds), all 810 tests passing
https://claude.ai/code/session_01Twt3D1bsEK3eXUhMaz6ee7
* Code review fixes: type safety, abstraction, and React correctness
- Derive PlayoffMatchGameStatus from schema enum instead of hardcoding
the string union, eliminating the duplicate source of truth
- updateGame now returns PlayoffMatchGame | undefined to reflect reality
when no row matches the ID
- Remove TOCTOU check-then-act in update-game action: call updateGame
directly and check the return value instead of a pre-flight findGameById
- Add status enum validation before the cast in update-game action
- Move impliedProbability computation inside upsertMatchOdds so callers
only provide moneylineOdds; the model owns the derivation
- Remove unnecessary dynamic import of americanToImpliedProbability in
the upsert-odds action (was already imported from the same module)
- Fix React list reconciliation bug: replace bare <> fragment with
<Fragment key={match.id}> so React can correctly track rows
https://claude.ai/code/session_01Twt3D1bsEK3eXUhMaz6ee7
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-11 14:17:43 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 40,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773262080928,
|
|
|
|
|
"tag": "0040_fat_puma",
|
|
|
|
|
"breakpoints": true
|
2026-03-15 10:22:42 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 41,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773557500336,
|
|
|
|
|
"tag": "0041_great_magma",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 42,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773600000000,
|
|
|
|
|
"tag": "0042_migrate_event_date_to_starts_at",
|
|
|
|
|
"breakpoints": true
|
2026-03-15 21:52:47 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 43,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773634394633,
|
|
|
|
|
"tag": "0043_demonic_vanisher",
|
|
|
|
|
"breakpoints": true
|
Add NCAAM and NCAAW bracket Monte Carlo simulators (#150)
- NCAAM: KenPom AEM logistic formula (1/(1+exp(-diff/7.5))), data through 2025-26 March 15
- NCAAW: Barttorvik Barthag Log5 formula (A*(1-B)/(A*(1-B)+B*(1-A))), same bracket structure
- Both simulators: 50,000-iteration Monte Carlo, First Four simulation, honors completed matches
- Track E8+ placements only: champion, finalist, FF losers (3rd/4th), E8 losers (5th–8th)
- Add bracket configuration validation: null R64 slots must exactly match First Four mapping
- Fix DEFAULT_SCORING_RULES to 100/70/45/45/20/20/20/20 (3rd/4th=45, 5th–8th=20)
- Align scoring constants across simulate route, expected-values display, and server action
- Zero out EVs for non-bracket participants on every simulation run (prevents EV inflation)
- Add EV total invariant warning (expected ~340) on expected-values admin page
- 98 unit tests across NCAAM, NCAAW, and UCL simulators — all passing
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:31:47 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 44,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773700000000,
|
|
|
|
|
"tag": "0044_add_ncaam_bracket_simulator_type",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 45,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773710000000,
|
|
|
|
|
"tag": "0045_add_ncaaw_bracket_simulator_type",
|
|
|
|
|
"breakpoints": true
|
2026-03-16 22:18:50 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 46,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773720000000,
|
|
|
|
|
"tag": "0046_add_nba_bracket_simulator_type",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 47,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773730000000,
|
|
|
|
|
"tag": "0047_fix_nba_bracket_simulator_type",
|
|
|
|
|
"breakpoints": true
|
Partial bracket scoring, code review fixes, and double-chance logic (#156)
## Partial bracket scoring
- `processMatchResult`: new exported function that scores a single match
immediately (loser → final placement, winner → provisional floor).
Called from `set-winner` and `set-round-winners` so points are awarded
as soon as a winner is set, before the full round is complete.
- `set-winner`: passes `eventName` to `processMatchResult`.
- `set-round-winners`: batches side effects — calls `recalculateAffectedLeagues`
and `updateProbabilitiesAfterResult` once after the loop instead of per-match
(`skipSideEffects: true` per match).
## Code review fixes
- **ROUND_CONFIG** (S1): extracted a `RoundScoringConfig` lookup table +
`getRoundConfig()` helper, eliminating three parallel `if/else` chains in
`processPlayoffEvent`, `processMatchResult`, and `getGuaranteedMinimumPosition`.
AFL template overrides live in `TEMPLATE_ROUND_CONFIG` with an explanatory
comment about why the `bracketTemplateId` guard is required.
- **skipSideEffects** (C2): new param on `processMatchResult`; bracket server
uses it to batch standings/probability recalc in `set-round-winners`.
- **eventName** (W1): passed through `processMatchResult` → `recalculateAffectedLeagues`.
- **Round validation** (W2): `set-round-winners` now guards `match.round === round`
before processing each assignment.
- **Comments** (C3/S3/W3): added notes on non-scoring loser assumption,
`isScoring ?? true` default, and AFL Semi-Finals template requirement.
## PlayoffBracket eliminated-teams fix + tests
- Fixed `computeEliminatedByRound` to track participant *appearances* (not just
wins), so AFL QF losers who advance to Semi-Finals via double-chance are
correctly excluded from the QF eliminated list.
- Extracted the logic as an exported pure function for testability.
- Added 4 tests: standard elimination, AFL QF loser → SF win, AFL QF loser →
SF loss, and normal advancement not protecting a later loser.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 10:50:30 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 48,
|
|
|
|
|
"version": "7",
|
2026-03-17 11:16:36 -07:00
|
|
|
"when": 1773765768905,
|
|
|
|
|
"tag": "0048_eager_songbird",
|
Partial bracket scoring, code review fixes, and double-chance logic (#156)
## Partial bracket scoring
- `processMatchResult`: new exported function that scores a single match
immediately (loser → final placement, winner → provisional floor).
Called from `set-winner` and `set-round-winners` so points are awarded
as soon as a winner is set, before the full round is complete.
- `set-winner`: passes `eventName` to `processMatchResult`.
- `set-round-winners`: batches side effects — calls `recalculateAffectedLeagues`
and `updateProbabilitiesAfterResult` once after the loop instead of per-match
(`skipSideEffects: true` per match).
## Code review fixes
- **ROUND_CONFIG** (S1): extracted a `RoundScoringConfig` lookup table +
`getRoundConfig()` helper, eliminating three parallel `if/else` chains in
`processPlayoffEvent`, `processMatchResult`, and `getGuaranteedMinimumPosition`.
AFL template overrides live in `TEMPLATE_ROUND_CONFIG` with an explanatory
comment about why the `bracketTemplateId` guard is required.
- **skipSideEffects** (C2): new param on `processMatchResult`; bracket server
uses it to batch standings/probability recalc in `set-round-winners`.
- **eventName** (W1): passed through `processMatchResult` → `recalculateAffectedLeagues`.
- **Round validation** (W2): `set-round-winners` now guards `match.round === round`
before processing each assignment.
- **Comments** (C3/S3/W3): added notes on non-scoring loser assumption,
`isScoring ?? true` default, and AFL Semi-Finals template requirement.
## PlayoffBracket eliminated-teams fix + tests
- Fixed `computeEliminatedByRound` to track participant *appearances* (not just
wins), so AFL QF losers who advance to Semi-Finals via double-chance are
correctly excluded from the QF eliminated list.
- Extracted the logic as an exported pure function for testability.
- Added 4 tests: standard elimination, AFL QF loser → SF win, AFL QF loser →
SF loss, and normal advancement not protecting a later loser.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 10:50:30 -07:00
|
|
|
"breakpoints": true
|
Grant sitewide admins commissioner-level access in leagues (#162)
* Grant sitewide admins commissioner-level access in leagues
- `isCommissioner()` now returns true for site admins, covering all
commissioner-gated loaders (league home, settings, sport season detail)
and draft API routes (start, pause, resume, rollback, replace-pick,
force-autopick, force-manual-pick, adjust-time-bank, make-pick)
- Added `hasCommissionerRecord()` (DB-only, no admin bypass) for the
"already a commissioner" duplicate-entry check in the settings action,
preventing a false positive when adding a site admin as commissioner
- `isCommissioner()` now runs the admin check and DB query in parallel
via Promise.all to avoid a serial roundtrip on every check
- Added "admin" to the `picked_by_type` enum (migration 0049) so picks
forced by a site admin are recorded accurately in the audit log rather
than as "commissioner"
- 8 unit tests covering both isCommissioner and hasCommissionerRecord
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix draft.force-manual-pick tests broken by isUserAdminByClerkId
The route now calls isUserAdminByClerkId which hits database().query.users,
but the test's mock DB had no query.users entry. Add a vi.mock for
~/models/user and default isUserAdminByClerkId to false in beforeEach.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 00:41:56 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 49,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773800000000,
|
|
|
|
|
"tag": "0049_add_admin_picked_by_type",
|
|
|
|
|
"breakpoints": true
|
2026-03-18 01:23:53 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 50,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773821310661,
|
|
|
|
|
"tag": "0050_chief_peter_quill",
|
|
|
|
|
"breakpoints": true
|
2026-03-18 22:15:28 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 51,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773900000000,
|
|
|
|
|
"tag": "0051_add_standings_snapshot_unique_index",
|
|
|
|
|
"breakpoints": true
|
2026-03-18 22:40:19 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 52,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773897678653,
|
|
|
|
|
"tag": "0052_new_warbird",
|
|
|
|
|
"breakpoints": true
|
2026-03-20 21:36:39 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 53,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1773903201189,
|
|
|
|
|
"tag": "0053_smooth_kingpin",
|
|
|
|
|
"breakpoints": true
|
2026-03-21 00:12:01 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 54,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774070769112,
|
|
|
|
|
"tag": "0054_massive_vulcan",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 55,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774072156237,
|
|
|
|
|
"tag": "0055_special_vampiro",
|
|
|
|
|
"breakpoints": true
|
2026-03-22 01:57:39 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 56,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774167142673,
|
|
|
|
|
"tag": "0056_jittery_the_fallen",
|
|
|
|
|
"breakpoints": true
|
2026-03-23 08:24:28 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 57,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774239259550,
|
|
|
|
|
"tag": "0057_cultured_doctor_doom",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 58,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774310000000,
|
|
|
|
|
"tag": "0058_add_unique_participant_ev",
|
|
|
|
|
"breakpoints": true
|
Add tennis Grand Slam simulator with surface Elo ratings, fixes #116 (#216)
Implements a Monte Carlo simulator for men's/women's tennis seasons scored
on the qualifying_points pattern. Simulates all 4 Grand Slam majors
(Australian Open, French Open, Wimbledon, US Open) using surface-specific
Elo ratings and ATP/WTA world rankings for seeding.
New table: participant_surface_elos — one row per (participant, season)
storing worldRanking, eloHard, eloClay, eloGrass.
Key design decisions:
- Seeding uses ATP/WTA world ranking (not Elo), matching real draw procedure
- Top 32 seeded with standard slot placement (1→0, 2→64, 3-4→quarters, etc.)
- QP per round with tie-splitting pre-applied: W=20, F=14, SF=9, QF=4, R16=1.5
- Completed majors read actual qualifyingPointsAwarded from eventResults
- 10,000 Monte Carlo simulations; column sums naturally 1.0 (no normalization)
Admin UI at /admin/sports-seasons/:id/surface-elo:
- 5-column grid (Player | Rank | Hard | Clay | Grass)
- Bulk import: "Name, ranking, hardElo, clayElo, grassElo" one per line
- Fuzzy name matching (bigram Dice coefficient) with "Did you mean?" suggestions
- Inline participant creation for unmatched names via useFetcher
- Saves Elos and auto-runs simulation on submit
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:59:35 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 59,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774329951326,
|
|
|
|
|
"tag": "0059_mysterious_jack_murdock",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 60,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774330910098,
|
|
|
|
|
"tag": "0060_omniscient_outlaw_kid",
|
|
|
|
|
"breakpoints": true
|
Add golf qualifying points simulator (Plackett-Luce Monte Carlo) (#223)
* Add golf QP simulator with Plackett-Luce model, fixes #120
- New `participant_golf_skills` table (migration 0061) for SG: Total and
per-major American odds per player/season
- New `app/models/golf-skills.ts` with getGolfSkillsMap, getGolfSkillsForSeason,
batchUpsertGolfSkills
- Full `GolfSimulator` implementation replacing the TODO stub: Plackett-Luce
ranking model (PL_BETA=1.5, FIELD_SIZE=156), 10k Monte Carlo iterations,
awards QP by finishing position, ranks by total QP across all 4 majors
- New admin route `sports-seasons/:id/golf-skills` with bulk CSV import,
fuzzy name matching, per-player SG + per-major odds inputs; saves skills
and auto-runs simulation on submit
- Simulator dropdown on sport admin sorted alphabetically; renamed to
"Golf Qualifying Points Monte Carlo"
- Golf Skills button shown on sports season admin when simulator type is
golf_qualifying_points
- Extract normalizeName/diceCoefficient to shared `app/lib/fuzzy-match.ts`,
removing duplication from surface-elo and golf-skills routes
- Parallelize 4 DB queries in GolfSimulator.simulate() with Promise.all
- O(1) field array removal via swap-to-end + pop (was O(N) splice)
- Fix source tag: performance_model (not elo_simulation) for SG-based model
- 23 unit tests covering americanToImplied, getMajorOddsKey, resolveSkill,
simulateMajor, and Monte Carlo calibration properties
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix oxlint errors: no-non-null-assertion and eqeqeq
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 21:46:02 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 61,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774410244601,
|
|
|
|
|
"tag": "0061_violet_mephistopheles",
|
|
|
|
|
"breakpoints": true
|
Add MLB playoff simulator with AL/NL division standings, fixes #121 (#225)
* Add MLB playoff simulator with AL/NL division standings, fixes #121
- New `mlb-simulator.ts`: 50k Monte Carlo sim drawing division winners
(weighted by p_div) and 3 WC teams per league, then simulating
WC best-of-3 → DS best-of-5 → LCS best-of-7 → World Series.
Elo parity factor 350; blends Vegas odds 30/70 when sourceOdds available.
- New `standings-sync/mlb.ts`: adapter for free statsapi.mlb.com API,
mapping AL/NL conferences, division ranks, streaks, home/away/L10 splits.
- New `mlb-divisions` display mode in RegularSeasonStandings: shows 1 division
winner per division section + Wild Card section with 3-spot playoff line,
headings read "AL/NL League" instead of "Conference".
- Refactored buildNhlSections/buildMlbSections into shared buildDivisionSections
(divisionSpots + wcSpots params); conferenceLabel now flows through group
objects rather than being derived from displayMode in the render.
- DB migration 0062 adds `mlb_bracket` to simulator_type enum.
- 37 new tests across simulator, adapter, and component.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix flaky tennis simulator test: increase trials and lower threshold
500 trials with a ~3–5% expected win rate had enough variance to
occasionally land below the 0.03 threshold (~2% failure rate).
Bumping to 2000 trials reduces the std dev by 2x; lowering the
threshold to 0.02 keeps the assertion meaningful (still well above
random 0.78%) while eliminating the flakiness.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 08:47:02 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 62,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774500000000,
|
|
|
|
|
"tag": "0062_add_mlb_bracket_simulator_type",
|
|
|
|
|
"breakpoints": true
|
2026-03-26 00:34:51 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 63,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774504559479,
|
|
|
|
|
"tag": "0063_bored_ultimo",
|
|
|
|
|
"breakpoints": true
|
2026-03-27 01:09:04 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 64,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774598151282,
|
|
|
|
|
"tag": "0064_fuzzy_wolfsbane",
|
|
|
|
|
"breakpoints": true
|
Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator (#242)
* Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator, fixes #127
- New `groupStageMatches` table for recording group play results (W/D/L, scores, matchday, schedule)
- `computeGroupStandings()` model function: pts → GD → GF → name tiebreaker ordering
- `GroupStageStandings` component showing all 12 groups with standings table and manager column
- Admin bracket UI: group match score entry, per-group standings, "Recalculate Floors" action
- `WorldCupSimulator`: 50k Monte Carlo covering group stage + best-8 3rd-place + knockout + 3rd place game
- Fuzzy name matching for national team Elo lookup (exact → substring → word-overlap), warns on miss
- Partial group completion: completed matches replayed with real scores, remaining matches simulated
- Elo priority: admin-entered sourceElo > futures odds converted to Elo > hardcoded national team ratings
- `fifa_48` bracket template: added Third Place Game round with `loserFeedsInto` on Semifinals
- Scoring rules: distinct 3rd/4th place for `fifa_48` (not averaged), QF losers share 5th–8th equally
- Floor scoring: SF participants guaranteed 4th (provisional), finalized after 3rd place game
- `recalculate-floors` admin action deletes and replays all results from scratch (fixes stale guard bug)
- Unique index on `(tournamentGroupId, participant1Id, participant2Id)` to prevent duplicate pairings
- Batch `findMatchesByGroupIds()` replacing N sequential queries in the sport season loader
- League home mini-standings now shows `actualPoints` (includes floor) instead of `totalPoints` only
- Elo ratings admin page supports World Cup (same bulk-import flow as snooker)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix TypeScript errors: update GroupStandingData type to use findMatchesByGroupIds
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Increase Node heap to 4GB for unit tests in CI to prevent OOM
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix OOM in CI: make WorldCupSimulator simulation count configurable for tests
Tests now pass numSimulations=500 instead of the production default of 50,000.
Six simulator tests × 50k iterations each was exhausting the 4GB heap on GitHub
Actions runners. Also reduce simGroupMatch stat tests from 50k to 5k iterations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 10:27:47 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 65,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774719902130,
|
|
|
|
|
"tag": "0065_kind_mantis",
|
|
|
|
|
"breakpoints": true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 66,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774802533652,
|
|
|
|
|
"tag": "0066_stiff_siren",
|
|
|
|
|
"breakpoints": true
|
Add PDC World Darts Championship simulator with Elo-based bracket (#248)
Fixes #123
* Add PDC World Darts Championship simulator (128-player bracket)
- New DartsSimulator: 128-player single-elimination, 7 rounds with
PDC-accurate best-of-sets formats (bo3/bo5/bo5/bo7/bo7/bo11/bo13).
ELO_DIVISOR=500 via set-level Bernoulli model. Two simulation paths:
Path A (bracket drawn) simulates from actual DB matches; Path B
(pre-bracket) seeds top 32 by world ranking in fixed positions and
randomly draws the remaining 96 per simulation run (50,000 iterations).
- darts_bracket added to simulatorTypeEnum and simulator registry.
- world_ranking nullable integer column added to participant_expected_values
(migration 0067); batchSaveSourceElos now accepts and persists it.
- Admin route /admin/sports-seasons/:id/darts-elo: bulk import with format
"Player Name, 2099, 1" (name, Elo, optional world ranking), fuzzy name
matching, auto-runs simulation and updates EV/snapshots on save.
- DARTS_128 bracket template added (scoring starts at Quarterfinals).
- 30 unit tests: math helpers, bracket seeding structure, Path A/B integration.
https://claude.ai/code/session_01WZ6FjMmC2eBeZ1564M9Ruz
* Review fixes: pre-compute hot-loop invariants, import normalizeName
- Pre-compute seededSlots and getSeededMatchOrder(32) before the 50k
simulation loop — was being recomputed every iteration
- Pad unseeded pool to 96 once before the loop instead of inside it
- Inline bracket-building in the hot loop using pre-computed seededSlots;
removes the per-iteration call to buildR1Bracket/getSeededMatchOrder
- Remove dead SEEDED_R1_PAIRS constant (was never referenced)
- Import normalizeName from ~/lib/fuzzy-match instead of redefining it locally
https://claude.ai/code/session_01WZ6FjMmC2eBeZ1564M9Ruz
* Fix lint failures: unused vars, eqeqeq, toSorted
- Remove unused seededSet/unseededSet variables in test
- Replace != null with !== null && !== undefined (eqeqeq rule)
- Replace .sort() with .toSorted() in simulator and route (unicorn/no-array-sort)
https://claude.ai/code/session_01WZ6FjMmC2eBeZ1564M9Ruz
* Fix TS2552: restore seededSet declaration removed during lint fix
https://claude.ai/code/session_01WZ6FjMmC2eBeZ1564M9Ruz
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-31 15:37:28 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 67,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1774900000000,
|
|
|
|
|
"tag": "0067_darts_bracket",
|
|
|
|
|
"breakpoints": true
|
Add CS2 Major Qualifying Points simulator and stage management (#260)
* Add CS2 Major qualifying points simulator
Implements a full CS2 Major tournament simulator with:
- 3-stage Swiss format (Opening Bo1, Elimination Bo1/Bo3, Decider all Bo3)
+ Champions Stage 8-team single-elimination (QF Bo3, SF Bo3, GF Bo5)
- Monte Carlo simulation (10,000 iterations) accumulating QP across 2 majors/season
- Sampled 24-team field per iteration: top 12 guaranteed, remaining weighted by 1/rank
- Stage 3 exits (placements 9-16) sub-ranked by W-L record (2-3 > 1-3 > 0-3)
- Stage assignments stored per-event so actual field composition drives simulation
- Admin CS Elo form for entering team Elo + HLTV world rankings
- Admin CS2 stage setup page for assigning teams to stages and tracking advancement
- Database migration: cs2_major_qualifying_points enum value + cs2_major_stage_results table
- 24 unit tests covering all exported pure functions
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Consolidate Elo + ranking input into generic elo-ratings page
The darts-elo and cs-elo pages were unreachable from the admin nav,
which always links to the generic elo-ratings page. Extended elo-ratings
to conditionally show world ranking fields for simulator types that need
it (darts_bracket, cs2_major_qualifying_points), then deleted the
redundant sport-specific pages.
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Consolidate server postgres connections into one shared pool
Four separate postgres() clients were open simultaneously (app, timer,
snapshots, socket), each defaulting to 10 connections, exhausting the
database's max_connections limit. Replaced with a single shared lazy-
initialized client in server/db.ts using a Proxy to defer the
DATABASE_URL check until first use (preserving test compatibility).
Also bumps the CS2 Champions Stage stochastic test from 200 → 1000
iterations to eliminate flakiness.
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Fix and() bug and add Swiss loop safety guard
- cs2-major-stage.ts: markCs2StageEliminations and setCs2FinalPlacements
were using JS && instead of Drizzle and(), causing WHERE to filter only
by participantId (not scoringEventId), which would update rows across
all events instead of just the target event
- cs-major-simulator.ts: add break guard in simulateSwiss while loop to
prevent infinite loop if pairGroups returns no pairs
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Fix all remaining code review issues
- cs2-major-stage.ts: use schema column reference for stageEliminated
in markCs2StageEliminations instead of raw SQL string
- cs-major-simulator.ts: simulateOneMajor now locks in known stage
results when a stage is complete (8 recorded eliminations), only
simulating the remaining stages during live events
- admin event page: add CS2 Stage Setup button for cs2_major_qualifying_points
simulator types; expose simulatorType in server loader type cast
- cs2-setup.tsx: replace document.getElementById DOM manipulation with
React state (eliminatedChecked map) for checkbox show/hide logic;
remove unused stageMap and unassignedParticipants variables
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Fix oxlint errors: non-null assertions, sort→toSorted, unused vars
- cs-major-simulator.ts: replace 5 non-null assertions (!) with safe
optional chaining / if-guards; replace 6 .sort() with .toSorted()
- cs2-major-stage.ts: remove unused `inArray` import
- cs2-setup.tsx: remove unused `assignedIds` variable
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Fix flaky Champions Stage stochastic test
The makeTeams(8) helper creates only a 70-pt Elo spread (1800→1730).
With the Champions Stage bracket math this gives team-0 a ~19.6% win
rate — right at the 0.2 threshold, causing the test to fail ~63% of
the time in CI despite 1000 iterations.
Use 100-pt steps (1800→1100) instead, giving team-0 a ~40% win rate
and raising the assertion threshold to 0.25 for a clear safety margin.
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-05 13:40:05 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 68,
|
|
|
|
|
"version": "7",
|
2026-04-06 10:53:51 -04:00
|
|
|
"when": 1775485575205,
|
Add CS2 Major Qualifying Points simulator and stage management (#260)
* Add CS2 Major qualifying points simulator
Implements a full CS2 Major tournament simulator with:
- 3-stage Swiss format (Opening Bo1, Elimination Bo1/Bo3, Decider all Bo3)
+ Champions Stage 8-team single-elimination (QF Bo3, SF Bo3, GF Bo5)
- Monte Carlo simulation (10,000 iterations) accumulating QP across 2 majors/season
- Sampled 24-team field per iteration: top 12 guaranteed, remaining weighted by 1/rank
- Stage 3 exits (placements 9-16) sub-ranked by W-L record (2-3 > 1-3 > 0-3)
- Stage assignments stored per-event so actual field composition drives simulation
- Admin CS Elo form for entering team Elo + HLTV world rankings
- Admin CS2 stage setup page for assigning teams to stages and tracking advancement
- Database migration: cs2_major_qualifying_points enum value + cs2_major_stage_results table
- 24 unit tests covering all exported pure functions
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Consolidate Elo + ranking input into generic elo-ratings page
The darts-elo and cs-elo pages were unreachable from the admin nav,
which always links to the generic elo-ratings page. Extended elo-ratings
to conditionally show world ranking fields for simulator types that need
it (darts_bracket, cs2_major_qualifying_points), then deleted the
redundant sport-specific pages.
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Consolidate server postgres connections into one shared pool
Four separate postgres() clients were open simultaneously (app, timer,
snapshots, socket), each defaulting to 10 connections, exhausting the
database's max_connections limit. Replaced with a single shared lazy-
initialized client in server/db.ts using a Proxy to defer the
DATABASE_URL check until first use (preserving test compatibility).
Also bumps the CS2 Champions Stage stochastic test from 200 → 1000
iterations to eliminate flakiness.
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Fix and() bug and add Swiss loop safety guard
- cs2-major-stage.ts: markCs2StageEliminations and setCs2FinalPlacements
were using JS && instead of Drizzle and(), causing WHERE to filter only
by participantId (not scoringEventId), which would update rows across
all events instead of just the target event
- cs-major-simulator.ts: add break guard in simulateSwiss while loop to
prevent infinite loop if pairGroups returns no pairs
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Fix all remaining code review issues
- cs2-major-stage.ts: use schema column reference for stageEliminated
in markCs2StageEliminations instead of raw SQL string
- cs-major-simulator.ts: simulateOneMajor now locks in known stage
results when a stage is complete (8 recorded eliminations), only
simulating the remaining stages during live events
- admin event page: add CS2 Stage Setup button for cs2_major_qualifying_points
simulator types; expose simulatorType in server loader type cast
- cs2-setup.tsx: replace document.getElementById DOM manipulation with
React state (eliminatedChecked map) for checkbox show/hide logic;
remove unused stageMap and unassignedParticipants variables
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Fix oxlint errors: non-null assertions, sort→toSorted, unused vars
- cs-major-simulator.ts: replace 5 non-null assertions (!) with safe
optional chaining / if-guards; replace 6 .sort() with .toSorted()
- cs2-major-stage.ts: remove unused `inArray` import
- cs2-setup.tsx: remove unused `assignedIds` variable
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
* Fix flaky Champions Stage stochastic test
The makeTeams(8) helper creates only a 70-pt Elo spread (1800→1730).
With the Champions Stage bracket math this gives team-0 a ~19.6% win
rate — right at the 0.2 threshold, causing the test to fail ~63% of
the time in CI despite 1000 iterations.
Use 100-pt steps (1800→1100) instead, giving team-0 a ~40% win rate
and raising the assertion threshold to 0.25 for a clear safety margin.
https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-05 13:40:05 -07:00
|
|
|
"tag": "0068_cs2_major_simulator",
|
|
|
|
|
"breakpoints": true
|
2026-04-05 19:09:52 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 69,
|
|
|
|
|
"version": "7",
|
2026-04-06 10:53:51 -04:00
|
|
|
"when": 1775485599229,
|
2026-04-05 19:09:52 -07:00
|
|
|
"tag": "0069_draft_window",
|
|
|
|
|
"breakpoints": true
|
2026-04-06 10:53:51 -04:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 70,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1775485630297,
|
|
|
|
|
"tag": "0070_verify_migrations",
|
|
|
|
|
"breakpoints": true
|
2026-04-07 09:39:34 -04:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"idx": 71,
|
|
|
|
|
"version": "7",
|
|
|
|
|
"when": 1775528386356,
|
|
|
|
|
"tag": "0071_many_nico_minoru",
|
|
|
|
|
"breakpoints": true
|
2025-10-10 23:04:50 -07:00
|
|
|
}
|
|
|
|
|
]
|
2026-04-07 09:39:34 -04:00
|
|
|
}
|