From 6e93c47a8e664bed3d2fcda199862d240a591697 Mon Sep 17 00:00:00 2001 From: Chris Hondl Date: Fri, 10 Jul 2026 08:07:36 -0700 Subject: [PATCH 1/2] Fix Strength of Schedule crash when pre-event EPAs are identical Floor the EPA-percentile Gaussian variance so it is always > 0. When every team at an event shares the same pre-event start EPA (true for all events early in a season, before ratings diverge), epaSd is 0, so the variance (epaSd^2 * 5 / N) is 0 -- or NaN when floating-point error makes the variance argument to Math.sqrt slightly negative. gaussian() throws on variance <= 0, which rejected the un-awaited strengthOfSchedule() promise in the worker and left the SOS table blank; the NaN path instead rendered NaN in the EPA/Composite columns. Both are the same degeneracy. '|| 1e-9' treats 0 and NaN alike, yielding a neutral 0.5 EPA percentile, and leaves every real positive variance untouched. --- frontend/src/pagesContent/event/[event_id]/worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pagesContent/event/[event_id]/worker.ts b/frontend/src/pagesContent/event/[event_id]/worker.ts index 3300ebe8..b82e9205 100644 --- a/frontend/src/pagesContent/event/[event_id]/worker.ts +++ b/frontend/src/pagesContent/event/[event_id]/worker.ts @@ -570,7 +570,7 @@ async function _strengthOfSchedule(data: EventData, simCount: number, postEvent: currTeamOpponents.length; const deltaEPA = epaAvg + 2 * avgPartnerEPA - 3 * avgOpponentEPA; - const distrib = Gaussian(0, (epaSd * epaSd * 5) / N); + const distrib = Gaussian(0, (epaSd * epaSd * 5) / N || 1e-9); const epaPercentile = 1 - distrib.cdf(deltaEPA); const overallPercentile = (rankPercentile + rpPercentile + epaPercentile) / 3; From 75151b8c3dca3d56a0ffa390e88742e635faaaaa Mon Sep 17 00:00:00 2001 From: Chris Hondl Date: Fri, 10 Jul 2026 08:12:45 -0700 Subject: [PATCH 2/2] Fix EPA column showing 0 on the event Simulation tab Read the team EPA from epa.breakdown.total_points, the field the SOS tab and the simulation worker already use. simulation.tsx read epa.total_points.mean, which matches the stale APITeamEvent type but not the runtime shape: the backend serves epa.total_points as a plain number, so .mean is undefined and every row rendered EPA 0. --- frontend/src/pagesContent/event/[event_id]/simulation.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/pagesContent/event/[event_id]/simulation.tsx b/frontend/src/pagesContent/event/[event_id]/simulation.tsx index 9fbd8e36..d12084c9 100644 --- a/frontend/src/pagesContent/event/[event_id]/simulation.tsx +++ b/frontend/src/pagesContent/event/[event_id]/simulation.tsx @@ -137,7 +137,7 @@ const SimulationSection = ({ eventId, data }: { eventId: string; data: EventData rank: i + 1, num: teamEvent.team, team: teamEvent?.team_name, - epa: round(teamEvent?.epa?.total_points?.mean ?? 0, 1), + epa: round(teamEvent?.epa?.breakdown?.total_points ?? 0, 1), rankMean: rankMean[teamEvent.team] ? round(rankMean[teamEvent.team], 2) : "", rank5: rank5[teamEvent.team] ? round(rank5[teamEvent.team], 2) : "", rank50: rank50[teamEvent.team] ? round(rank50[teamEvent.team], 2) : "", @@ -158,7 +158,7 @@ const SimulationSection = ({ eventId, data }: { eventId: string; data: EventData return { num: teamEvent.team, team: teamEvent?.team_name, - epa: round(teamEvent?.epa?.total_points?.mean ?? 0, 1), + epa: round(teamEvent?.epa?.breakdown?.total_points ?? 0, 1), rankMean: rankMean[teamEvent.team] ? round(rankMean[teamEvent.team], 2) : "", RPMean: RPMean[teamEvent.team] ? round(RPMean[teamEvent.team], 2) : "", ...probsObj,