Three review follow-ups on the conditioning change:
- reconcileAdvancers no longer flips a result that is locked in from real
match data. Locked advancers are kept out of the demotion pool and
locked-eliminated teams out of the promotion pool; they are only touched
as a last resort when there aren't enough non-locked teams to reach the
8-advancer target (a genuinely inconsistent snapshot). buildStageInitialRecords
now also returns the lockedAdvanced set for this.
- simulateChampionsStage only honors recorded bracket results when the real
bracket pairings are actually in use (realBracket). Previously a recorded
winner could be applied to a fictitious Elo-seeded pairing.
- Per-stage Swiss W-L reconstruction is loop-invariant, so it is now computed
once per event in CSMajorSimulator.simulate and passed in via
options.swissRecords instead of being re-derived on every Monte Carlo
iteration.
Adds direct unit coverage for reconcileAdvancers (protection + last-resort
fallback) and for the bracket-gating behavior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018zMG6JFng8bMqZYweffVuE
The CS2 Major projected-points simulator re-simulated outcomes that had
already happened, so its projections drifted from reality as a major
played out. Specifically it ignored the Champions Stage bracket entirely
(re-simulating the whole 8-team playoff each iteration), only locked in
Swiss stages all-or-nothing once 8 eliminations were recorded, seeded the
bracket by world rank rather than real Stage 3 performance, and read
qualifying points only from fully-completed events.
This conditions each simulation on whatever is already known and only
randomizes the undecided remainder:
- Champions Stage now honors the real bracket (playoffMatches): actual QF
pairings supply the seeding, and any completed QF/SF/Final result is used
verbatim instead of re-simulated. Falls back to Elo seeding when no
bracket exists.
- Swiss stages reconstruct each team's current W-L from real match results
(seasonMatches) so partly-played stages lock in their decided games and
only the remaining teams are simulated; generalize simulateSwiss to start
from seeded records.
- Provisional QP already written to event_results for settled (eliminated)
teams is used verbatim.
- reconcileAdvancers keeps each stage at exactly 8 advancers so ragged
mid-stage snapshots can't produce malformed downstream stages.
Adds unit coverage for bracket honoring (full and partial), partial-stage
locking, and recorded-QP override.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018zMG6JFng8bMqZYweffVuE