Replace inline import() type annotations with top-level `import type * as`
(consistent-type-imports) and use toSorted() over sort() (unicorn/no-array-sort)
in the tests added for diffChangedQualifyingPoints.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hq3fG4Zn3unntbSQAGSqCq
The before/after QP diff (with its null = "no prior award" and null → value
"first-time score" semantics) was duplicated in processQualifyingEvent and
rescoreTennisBracketAndDetectChanges. The two entry points must stay separate
(tennis snapshots before an authoritative delete-all; CS2 preserves rows), but
the comparison itself should live in one place.
Add diffChangedQualifyingPoints(before, after) in qualifying-points.ts and call
it from both paths. Unit-tested directly; the two integration tests still cover
the end-to-end behavior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hq3fG4Zn3unntbSQAGSqCq
processQualifyingEvent announced the full QP standings on every run. In a
multi-season shared major (e.g. Wimbledon), syncTournamentResults re-scores
each sibling window on every fan-out sync, so sibling-window leagues received
a duplicate full-standings ping each run even when nothing changed — the same
re-spam the primary tennis path already avoids.
Snapshot each participant's awarded QP before reprocessing and, after the
re-score, diff against the fresh rows to notify only participants whose QP
changed (a differing value, or a first-time score). This centralizes the
change-detection in the shared scoring path, so every sibling window and the
admin scoring/bracket routes get the quiet-on-no-change behavior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hq3fG4Zn3unntbSQAGSqCq
The tennis Grand Slam draw sync (`syncTennisDraw`) scores its primary
window by calling `processQualifyingBracketEvent` directly, which writes
QP but never emits the qualifying-points Discord notification (only
`processQualifyingEvent` and the CS2 stage scorer did). As a result,
running the Wimbledon sync produced no QP notifications for the season
that leagues actually draft from.
Extract the rescore into `rescoreTennisBracketAndDetectChanges`, which
snapshots each participant's awarded QP before rebuilding results and
reports whose QP changed. `syncTennisDraw` then calls
`notifyQualifyingPointsUpdate` for the primary window, scoped to the
changed participants so a re-run that changes nothing announces nothing.
The webhook call runs outside the rescore transaction.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hq3fG4Zn3unntbSQAGSqCq