- Replace hardcoded FanGraphs p_div/p_wc draws with Binomial regular-season
simulation so that admin-entered projected wins drive playoff qualification
odds, not just in-bracket game win probability
- Add getRegularSeasonStandings call so mid-season current wins feed into
projected final standings
- Use normal approximation (Box-Muller) in sampleBinomial for n≥30: drops
seeding phase from ~243M to ~3M Math.random() calls per 50K-sim run
- eloToRDif now calls rawWinRateFromElo instead of inlining the same formula
- Warn when recognized teams lack a standings row during mid-season simulation
(silent wrong seeding result with no diagnostic previously)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a simulation action threw (e.g. readiness check failure), React Router
tried to render the simulate route via GET to display the error — but the
route had no loader, so the real error was completely swallowed by React
Router's own 400 "no loader" message.
Four issues fixed:
- Move simulate into the intent-dispatch pattern on the season page (matching
delete/rescore/sync-standings/finalize-standings), so errors surface via
actionData instead of being lost
- Eliminate the ?simulationError= query param approach, which left stale error
messages in browser history and reflected user-controlled text in the UI
- Match the existing bg-destructive/15 error styling used elsewhere on the page
- Strip the now-unused action from simulate.tsx; keep a loader-only redirect
stub so any bookmarked URLs degrade gracefully to the season page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>