Winning a round in a major bracket (CS2 Champions Stage) now immediately
awards the team the guaranteed-minimum qualifying points for the position
they've locked in — e.g. a QF win guarantees at least a T3 finish (9 QP),
an SF win guarantees 2nd (14 QP), and a Final win earns 1st (20 QP).
This also fixes a bug where CS2 majors (a qualifying_points sport) wrongly
banked actual fantasy points: the generic bracket admin routed results
through processMatchResult/processPlayoffEvent, which write the fantasy
placement table (seasonParticipantResults). For qualifying sports, fantasy
points must come only from finalizeQualifyingPoints across all majors.
- scoring-calculator.ts: add deriveBracketQualifyingStates (pure) and
processQualifyingBracketEvent, deriving each team's (placement, tieCount)
floor from playoffMatches via the existing ROUND_CONFIG and writing QP to
event_results. Export getRoundConfig for testing.
- bracket.server.ts: branch all five write handlers on isQualifyingEvent so
qualifying brackets are scored via the QP path and never write fantasy
placements. reprocess-bracket additionally clears stale
seasonParticipantResults rows, making it the cleanup tool for majors that
already banked points under the old path.
- Add qualifying-bracket-scoring.test.ts covering the floor derivation,
partial/un-set brackets, idempotency, and the worked QP example.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVLqaYGXCeSYYqTHZmbRZi