Four related bugs in the Qualifying Points subsystem surfaced on Wimbledon:
1. Some leagues stored 2 QP instead of 1.5 for Round-of-16 losers. Sibling/
mirror windows scored via the placement-group path in processQualifyingEvent,
which took the tie span from the players present on that window's roster
(a draftable subset) instead of the full field. A window holding fewer than
the 8 tied R16 losers split the 9-16 points too few ways and over-awarded.
The tie span is now derived from the canonical tournament_results (the whole
field) for tournament-linked events, falling back to the live count only for
standalone events. Every league now scores identically.
2. Discord notifications rounded QP with Math.round, turning 1.5 into "2".
Both the "Points Awarded" and "QP Standings" values now use a 2-decimal
formatter mirroring the web UI's formatQP, so Discord and the site agree.
3. The Discord "QP Standings" block ranked players only among that event's
scorers, showing two R16 losers as T1 instead of T9. Rank is now computed
over the full season field and passed through to the notifier.
4. "N of M majors completed" reached "11 of 4": majorsCompleted was a stored
counter incremented on every fan-out sync with a guard that misfired. It is
now derived on read (getMajorsCompleted = count of completed qualifying
events), which is self-correcting; the increment/decrement writes are removed
along with the now-unused hasProcessedQualifyingPlacement helper.
Adds scripts/backfill-qp-resplit.ts to silently re-score existing majors so
already-corrupted seasons are corrected (2 -> 1.5), and threads a
skipNotifications option through processQualifyingEvent / syncTournamentResults
so the backfill does not re-ping every league.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017AUcHy9m7aF6axsY6Grpe4
* Add oxlint and fix all lint errors
- Install oxlint, add .oxlintrc.json with rules for TypeScript/React
- Add npm run lint / lint:fix scripts
- Add Claude PostToolUse hook to run oxlint on every edited file
- Fix 101 errors: unused vars/imports, eqeqeq, prefer-const, no-new-array
- Fix no-array-index-key (use stable keys or suppress positional cases)
- Fix exhaustive-deps missing dependency in useEffect
- Promote exhaustive-deps and no-array-index-key to errors
- Fix Map.get() !== null bug in $leagueId.server.ts (should be !== undefined)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix no-explicit-any warnings and upgrade tsconfig to ES2023
- Replace all `any` types with proper types or `unknown` across ~20 files
- Add typed socket payload interfaces in draft route and useDraftSocket
- Use any[] with eslint-disable for socket.io callbacks (legitimate escape hatch)
- Bump all tsconfigs from ES2022 → ES2023 to support toSorted/toReversed
- Fix cascading type errors uncovered by removing any: Map.get narrowing,
participant relation types, ChartDataPoint, Partial<NewSeason> indexing
- Add ParticipantResultWithParticipant type to participant-result model
- Fix test fixtures to match updated interfaces (DraftCell, ParticipantResult)
- Fix duplicate getQPStandings import in sportsSeasonId.server.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Promote no-explicit-any to error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added `QualifyingPointsStandings` component to display standings based on qualifying points.
- Introduced scoring rules and projected points calculation for participants.
- Implemented tie handling for rankings and displayed appropriate UI elements based on finalization status.
- Created tests for qualifying points configuration, accumulation logic, ranking logic, and scoring workflow.
- Developed scoring calculator tests to validate fantasy points conversion from qualifying points.
- Established qualifying points management functions including initialization, retrieval, updating, and resetting of points.