Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
import { getSimulatorInfo, SIMULATOR_TYPES, type SimulatorType } from "./registry";
|
|
|
|
|
|
|
|
|
|
export type SimulatorInputKey =
|
|
|
|
|
| "sourceOdds"
|
|
|
|
|
| "sourceElo"
|
|
|
|
|
| "worldRanking"
|
|
|
|
|
| "rating"
|
|
|
|
|
| "projectedWins"
|
|
|
|
|
| "projectedTablePoints"
|
|
|
|
|
| "seed"
|
|
|
|
|
| "region"
|
|
|
|
|
| "metadata";
|
|
|
|
|
|
|
|
|
|
export type SimulatorSetupSection =
|
|
|
|
|
| "futuresOdds"
|
|
|
|
|
| "eloRatings"
|
|
|
|
|
| "rankings"
|
|
|
|
|
| "ratings"
|
|
|
|
|
| "regularStandings"
|
|
|
|
|
| "bracket"
|
|
|
|
|
| "events"
|
|
|
|
|
| "golfSkills"
|
|
|
|
|
| "surfaceElo"
|
|
|
|
|
| "cs2Setup"
|
|
|
|
|
| "participants";
|
|
|
|
|
|
|
|
|
|
export interface SimulatorManifestProfile {
|
|
|
|
|
simulatorType: SimulatorType;
|
|
|
|
|
displayName: string;
|
|
|
|
|
description: string;
|
|
|
|
|
defaultConfig: Record<string, unknown>;
|
|
|
|
|
requiredInputs: SimulatorInputKey[];
|
|
|
|
|
optionalInputs: SimulatorInputKey[];
|
|
|
|
|
derivableInputs?: Partial<Record<SimulatorInputKey, SimulatorInputKey[]>>;
|
|
|
|
|
setupSections: SimulatorSetupSection[];
|
|
|
|
|
minParticipantInputs?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const BASE_CONFIG = {
|
|
|
|
|
iterations: 50_000,
|
|
|
|
|
};
|
|
|
|
|
|
Unify futures odds with the simulation system
Make the futures-vs-Elo relationship an explicit, configurable rule and fix
futures odds failing to override stored Elo.
Root causes addressed:
- resolveSourceElos/resolveRatings used a hardcoded precedence (direct Elo ->
projectedWins -> projectedTablePoints -> odds). The prior fix nulled sourceElo
on futures entry but not projections, which still outranked odds.
- The override relied on a destructive null-on-save hack that also wiped
manually entered Elo.
- Blended simulators buried their Elo/odds weight in module constants.
- Futures odds were not surfaced on the /admin/simulators inventory.
Changes:
- Add a configurable source policy: sourceEloPriority (ordered) and oddsWeight,
parsed/clamped in getSimulatorInputPolicy. resolveSourceElos/resolveRatings now
resolve each participant by the configured priority instead of a fixed order.
Futures-centric simulators (ncaam, ncaaw, world_cup, ncaa_football,
college_hockey) default to a futures-override priority.
- Drop the destructive nulling in batchSaveFuturesOddsForSimulator and
batchSaveSourceOdds; override is now governed by policy, preserving stored Elo.
- Thread an optional SimulationContext (oddsWeight) through the Simulator
interface so blended sims (UCL, World Cup, NCAA FB, MLB) read the blend weight
from the season policy; defaults preserve prior calibration when no context is
passed.
- Add a "Futures vs. Elo" strategy control and Odds Blend Weight input to the
Simulator Setup input-policy card, persisted via save-input-policy.
- Surface futures on /admin/simulators: a source badge and a Futures Odds quick
link; extend listSportsSeasonSimulatorSummaries with odds source info.
- Tests: configurable priority override (Elo/projections/rating), oddsWeight
parsing/clamping, prefersFuturesOdds, manifest defaults, an NCAA Football
context-blend behavioral test, and an updated non-destructive save test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhNeB7gN6VKene7sdbBVQT
2026-06-25 22:11:32 +00:00
|
|
|
// Source-resolution priority that makes freshly entered futures odds win over a
|
|
|
|
|
// stored Elo/rating (and projections). Used as the default for simulators where
|
|
|
|
|
// futures odds are a deliberate, primary input (those exposing a Futures Odds
|
|
|
|
|
// setup section). Admins can still override this per season in the Input Policy.
|
|
|
|
|
// Kept as a local literal (not imported from input-policy) to avoid a circular
|
|
|
|
|
// module dependency that would be evaluated before input-policy's consts exist.
|
|
|
|
|
// Mirrors FUTURES_FIRST_SOURCE_PRIORITY in input-policy.ts.
|
|
|
|
|
const FUTURES_FIRST_PRIORITY = ["sourceOdds", "sourceElo", "projectedWins", "projectedTablePoints"] as const;
|
|
|
|
|
|
Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
const PROFILES: Record<SimulatorType, Omit<SimulatorManifestProfile, "simulatorType" | "displayName" | "description">> = {
|
|
|
|
|
f1_standings: {
|
|
|
|
|
defaultConfig: { iterations: 10_000, raceNoise: 0.5, participantVolatility: 1.5 },
|
|
|
|
|
requiredInputs: ["sourceOdds"],
|
|
|
|
|
optionalInputs: [],
|
|
|
|
|
setupSections: ["participants", "futuresOdds", "events"],
|
|
|
|
|
},
|
|
|
|
|
indycar_standings: {
|
|
|
|
|
defaultConfig: { iterations: 10_000, raceNoise: 0.5, participantVolatility: 1.5 },
|
|
|
|
|
requiredInputs: ["sourceOdds"],
|
|
|
|
|
optionalInputs: [],
|
|
|
|
|
setupSections: ["participants", "futuresOdds", "events"],
|
|
|
|
|
},
|
|
|
|
|
golf_qualifying_points: {
|
|
|
|
|
defaultConfig: { iterations: 10_000, fieldSize: 156, plBeta: 1.5 },
|
|
|
|
|
requiredInputs: [],
|
|
|
|
|
optionalInputs: ["sourceOdds", "rating"],
|
|
|
|
|
setupSections: ["participants", "events", "golfSkills"],
|
|
|
|
|
},
|
|
|
|
|
playoff_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG },
|
|
|
|
|
requiredInputs: ["sourceOdds"],
|
|
|
|
|
optionalInputs: ["sourceElo"],
|
|
|
|
|
setupSections: ["participants", "futuresOdds", "bracket"],
|
|
|
|
|
},
|
|
|
|
|
ucl_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG, eloWeight: 0.7, oddsWeight: 0.3 },
|
|
|
|
|
requiredInputs: ["sourceOdds"],
|
|
|
|
|
optionalInputs: ["sourceElo"],
|
|
|
|
|
setupSections: ["participants", "futuresOdds", "bracket"],
|
|
|
|
|
},
|
|
|
|
|
ncaam_bracket: {
|
Unify futures odds with the simulation system
Make the futures-vs-Elo relationship an explicit, configurable rule and fix
futures odds failing to override stored Elo.
Root causes addressed:
- resolveSourceElos/resolveRatings used a hardcoded precedence (direct Elo ->
projectedWins -> projectedTablePoints -> odds). The prior fix nulled sourceElo
on futures entry but not projections, which still outranked odds.
- The override relied on a destructive null-on-save hack that also wiped
manually entered Elo.
- Blended simulators buried their Elo/odds weight in module constants.
- Futures odds were not surfaced on the /admin/simulators inventory.
Changes:
- Add a configurable source policy: sourceEloPriority (ordered) and oddsWeight,
parsed/clamped in getSimulatorInputPolicy. resolveSourceElos/resolveRatings now
resolve each participant by the configured priority instead of a fixed order.
Futures-centric simulators (ncaam, ncaaw, world_cup, ncaa_football,
college_hockey) default to a futures-override priority.
- Drop the destructive nulling in batchSaveFuturesOddsForSimulator and
batchSaveSourceOdds; override is now governed by policy, preserving stored Elo.
- Thread an optional SimulationContext (oddsWeight) through the Simulator
interface so blended sims (UCL, World Cup, NCAA FB, MLB) read the blend weight
from the season policy; defaults preserve prior calibration when no context is
passed.
- Add a "Futures vs. Elo" strategy control and Odds Blend Weight input to the
Simulator Setup input-policy card, persisted via save-input-policy.
- Surface futures on /admin/simulators: a source badge and a Futures Odds quick
link; extend listSportsSeasonSimulatorSummaries with odds source info.
- Tests: configurable priority override (Elo/projections/rating), oddsWeight
parsing/clamping, prefersFuturesOdds, manifest defaults, an NCAA Football
context-blend behavioral test, and an updated non-destructive save test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhNeB7gN6VKene7sdbBVQT
2026-06-25 22:11:32 +00:00
|
|
|
defaultConfig: { ...BASE_CONFIG, ratingScaleFactor: 7.5, inputPolicy: { ratingMin: -10, ratingMax: 35, fallbackRatingDelta: 5, sourceEloPriority: FUTURES_FIRST_PRIORITY } },
|
Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
requiredInputs: ["rating"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "sourceElo", "seed", "region"],
|
|
|
|
|
derivableInputs: { rating: ["sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "ratings", "futuresOdds", "bracket"],
|
|
|
|
|
},
|
|
|
|
|
ncaaw_bracket: {
|
Unify futures odds with the simulation system
Make the futures-vs-Elo relationship an explicit, configurable rule and fix
futures odds failing to override stored Elo.
Root causes addressed:
- resolveSourceElos/resolveRatings used a hardcoded precedence (direct Elo ->
projectedWins -> projectedTablePoints -> odds). The prior fix nulled sourceElo
on futures entry but not projections, which still outranked odds.
- The override relied on a destructive null-on-save hack that also wiped
manually entered Elo.
- Blended simulators buried their Elo/odds weight in module constants.
- Futures odds were not surfaced on the /admin/simulators inventory.
Changes:
- Add a configurable source policy: sourceEloPriority (ordered) and oddsWeight,
parsed/clamped in getSimulatorInputPolicy. resolveSourceElos/resolveRatings now
resolve each participant by the configured priority instead of a fixed order.
Futures-centric simulators (ncaam, ncaaw, world_cup, ncaa_football,
college_hockey) default to a futures-override priority.
- Drop the destructive nulling in batchSaveFuturesOddsForSimulator and
batchSaveSourceOdds; override is now governed by policy, preserving stored Elo.
- Thread an optional SimulationContext (oddsWeight) through the Simulator
interface so blended sims (UCL, World Cup, NCAA FB, MLB) read the blend weight
from the season policy; defaults preserve prior calibration when no context is
passed.
- Add a "Futures vs. Elo" strategy control and Odds Blend Weight input to the
Simulator Setup input-policy card, persisted via save-input-policy.
- Surface futures on /admin/simulators: a source badge and a Futures Odds quick
link; extend listSportsSeasonSimulatorSummaries with odds source info.
- Tests: configurable priority override (Elo/projections/rating), oddsWeight
parsing/clamping, prefersFuturesOdds, manifest defaults, an NCAA Football
context-blend behavioral test, and an updated non-destructive save test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhNeB7gN6VKene7sdbBVQT
2026-06-25 22:11:32 +00:00
|
|
|
defaultConfig: { ...BASE_CONFIG, inputPolicy: { ratingMin: 0.70, ratingMax: 0.97, missingRatingStrategy: "worstKnownMinus", fallbackRatingDelta: 0.01, sourceEloPriority: FUTURES_FIRST_PRIORITY } },
|
Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
requiredInputs: ["rating"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "seed", "region"],
|
|
|
|
|
derivableInputs: { rating: ["sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "ratings", "futuresOdds", "bracket"],
|
|
|
|
|
},
|
|
|
|
|
nba_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG, parityFactor: 400, seasonGames: 82 },
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "projectedWins"],
|
|
|
|
|
derivableInputs: { sourceElo: ["projectedWins", "sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "regularStandings", "bracket"],
|
|
|
|
|
},
|
|
|
|
|
nhl_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG, parityFactor: 1000, seasonGames: 82, overtimeRate: 0.23 },
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "projectedWins"],
|
|
|
|
|
derivableInputs: { sourceElo: ["projectedWins", "sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "regularStandings", "bracket"],
|
|
|
|
|
},
|
|
|
|
|
nfl_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG, parityFactor: 400, seasonGames: 17, homeFieldElo: 48 },
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "projectedWins"],
|
|
|
|
|
derivableInputs: { sourceElo: ["projectedWins", "sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "regularStandings"],
|
|
|
|
|
},
|
|
|
|
|
afl_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG, parityFactor: 450, seasonGames: 23 },
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["projectedWins"],
|
|
|
|
|
derivableInputs: { sourceElo: ["projectedWins"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "regularStandings"],
|
|
|
|
|
},
|
|
|
|
|
epl_standings: {
|
2026-05-13 15:02:23 -07:00
|
|
|
defaultConfig: {
|
|
|
|
|
...BASE_CONFIG,
|
2026-05-31 17:39:43 +00:00
|
|
|
iterations: 10_000,
|
2026-05-13 15:02:23 -07:00
|
|
|
seasonGames: 38,
|
|
|
|
|
parityFactor: 400,
|
|
|
|
|
matchParityFactor: 400,
|
|
|
|
|
averageOpponentElo: 1500,
|
|
|
|
|
baseDrawRate: 0.26,
|
|
|
|
|
drawDecay: 0.002,
|
|
|
|
|
},
|
Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "projectedTablePoints"],
|
|
|
|
|
derivableInputs: { sourceElo: ["projectedTablePoints", "sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "regularStandings"],
|
|
|
|
|
},
|
|
|
|
|
snooker_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG, eloDivisor: 400 },
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["worldRanking", "seed"],
|
|
|
|
|
setupSections: ["participants", "eloRatings", "rankings", "bracket"],
|
|
|
|
|
},
|
|
|
|
|
tennis_qualifying_points: {
|
|
|
|
|
defaultConfig: { iterations: 10_000, eloDivisor: 400, fallbackElo: 1500 },
|
|
|
|
|
requiredInputs: [],
|
|
|
|
|
optionalInputs: ["worldRanking"],
|
|
|
|
|
setupSections: ["participants", "surfaceElo", "events"],
|
|
|
|
|
},
|
|
|
|
|
mlb_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG, rDiffWeight: 0.7, oddsWeight: 0.3, seasonGames: 162 },
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "projectedWins"],
|
|
|
|
|
derivableInputs: { sourceElo: ["projectedWins", "sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "regularStandings"],
|
|
|
|
|
},
|
|
|
|
|
wnba_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG, seasonGames: 44, srsEloScale: 30 },
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "projectedWins"],
|
|
|
|
|
derivableInputs: { sourceElo: ["projectedWins", "sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "regularStandings"],
|
|
|
|
|
},
|
|
|
|
|
world_cup: {
|
Unify futures odds with the simulation system
Make the futures-vs-Elo relationship an explicit, configurable rule and fix
futures odds failing to override stored Elo.
Root causes addressed:
- resolveSourceElos/resolveRatings used a hardcoded precedence (direct Elo ->
projectedWins -> projectedTablePoints -> odds). The prior fix nulled sourceElo
on futures entry but not projections, which still outranked odds.
- The override relied on a destructive null-on-save hack that also wiped
manually entered Elo.
- Blended simulators buried their Elo/odds weight in module constants.
- Futures odds were not surfaced on the /admin/simulators inventory.
Changes:
- Add a configurable source policy: sourceEloPriority (ordered) and oddsWeight,
parsed/clamped in getSimulatorInputPolicy. resolveSourceElos/resolveRatings now
resolve each participant by the configured priority instead of a fixed order.
Futures-centric simulators (ncaam, ncaaw, world_cup, ncaa_football,
college_hockey) default to a futures-override priority.
- Drop the destructive nulling in batchSaveFuturesOddsForSimulator and
batchSaveSourceOdds; override is now governed by policy, preserving stored Elo.
- Thread an optional SimulationContext (oddsWeight) through the Simulator
interface so blended sims (UCL, World Cup, NCAA FB, MLB) read the blend weight
from the season policy; defaults preserve prior calibration when no context is
passed.
- Add a "Futures vs. Elo" strategy control and Odds Blend Weight input to the
Simulator Setup input-policy card, persisted via save-input-policy.
- Surface futures on /admin/simulators: a source badge and a Futures Odds quick
link; extend listSportsSeasonSimulatorSummaries with odds source info.
- Tests: configurable priority override (Elo/projections/rating), oddsWeight
parsing/clamping, prefersFuturesOdds, manifest defaults, an NCAA Football
context-blend behavioral test, and an updated non-destructive save test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhNeB7gN6VKene7sdbBVQT
2026-06-25 22:11:32 +00:00
|
|
|
defaultConfig: { ...BASE_CONFIG, iterations: 10_000, eloWeight: 0.7, oddsWeight: 0.3, inputPolicy: { sourceEloPriority: FUTURES_FIRST_PRIORITY, oddsWeight: 0.3 } },
|
Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "worldRanking"],
|
|
|
|
|
derivableInputs: { sourceElo: ["sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "futuresOdds", "events"],
|
|
|
|
|
},
|
|
|
|
|
darts_bracket: {
|
2026-05-31 17:39:43 +00:00
|
|
|
defaultConfig: { ...BASE_CONFIG, iterations: 10_000, eloDivisor: 400 },
|
Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
requiredInputs: ["sourceElo", "worldRanking"],
|
|
|
|
|
optionalInputs: ["seed"],
|
|
|
|
|
setupSections: ["participants", "eloRatings", "rankings"],
|
|
|
|
|
},
|
|
|
|
|
cs2_major_qualifying_points: {
|
|
|
|
|
defaultConfig: { iterations: 10_000, fieldSize: 32, guaranteedCount: 12 },
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["worldRanking", "metadata"],
|
|
|
|
|
setupSections: ["participants", "eloRatings", "rankings", "cs2Setup", "events"],
|
|
|
|
|
},
|
|
|
|
|
ncaa_football_bracket: {
|
Unify futures odds with the simulation system
Make the futures-vs-Elo relationship an explicit, configurable rule and fix
futures odds failing to override stored Elo.
Root causes addressed:
- resolveSourceElos/resolveRatings used a hardcoded precedence (direct Elo ->
projectedWins -> projectedTablePoints -> odds). The prior fix nulled sourceElo
on futures entry but not projections, which still outranked odds.
- The override relied on a destructive null-on-save hack that also wiped
manually entered Elo.
- Blended simulators buried their Elo/odds weight in module constants.
- Futures odds were not surfaced on the /admin/simulators inventory.
Changes:
- Add a configurable source policy: sourceEloPriority (ordered) and oddsWeight,
parsed/clamped in getSimulatorInputPolicy. resolveSourceElos/resolveRatings now
resolve each participant by the configured priority instead of a fixed order.
Futures-centric simulators (ncaam, ncaaw, world_cup, ncaa_football,
college_hockey) default to a futures-override priority.
- Drop the destructive nulling in batchSaveFuturesOddsForSimulator and
batchSaveSourceOdds; override is now governed by policy, preserving stored Elo.
- Thread an optional SimulationContext (oddsWeight) through the Simulator
interface so blended sims (UCL, World Cup, NCAA FB, MLB) read the blend weight
from the season policy; defaults preserve prior calibration when no context is
passed.
- Add a "Futures vs. Elo" strategy control and Odds Blend Weight input to the
Simulator Setup input-policy card, persisted via save-input-policy.
- Surface futures on /admin/simulators: a source badge and a Futures Odds quick
link; extend listSportsSeasonSimulatorSummaries with odds source info.
- Tests: configurable priority override (Elo/projections/rating), oddsWeight
parsing/clamping, prefersFuturesOdds, manifest defaults, an NCAA Football
context-blend behavioral test, and an updated non-destructive save test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhNeB7gN6VKene7sdbBVQT
2026-06-25 22:11:32 +00:00
|
|
|
defaultConfig: { ...BASE_CONFIG, bracketSize: 12, eloWeight: 0.6, oddsWeight: 0.4, inputPolicy: { sourceEloPriority: FUTURES_FIRST_PRIORITY, oddsWeight: 0.4 } },
|
Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "worldRanking"],
|
|
|
|
|
derivableInputs: { sourceElo: ["sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "futuresOdds", "bracket"],
|
|
|
|
|
},
|
|
|
|
|
llws_bracket: {
|
|
|
|
|
defaultConfig: { ...BASE_CONFIG, usTeamCount: 10, internationalTeamCount: 10, poolSize: 5 },
|
|
|
|
|
requiredInputs: ["sourceOdds"],
|
|
|
|
|
optionalInputs: ["metadata"],
|
|
|
|
|
setupSections: ["participants", "futuresOdds"],
|
|
|
|
|
},
|
|
|
|
|
college_hockey_bracket: {
|
Unify futures odds with the simulation system
Make the futures-vs-Elo relationship an explicit, configurable rule and fix
futures odds failing to override stored Elo.
Root causes addressed:
- resolveSourceElos/resolveRatings used a hardcoded precedence (direct Elo ->
projectedWins -> projectedTablePoints -> odds). The prior fix nulled sourceElo
on futures entry but not projections, which still outranked odds.
- The override relied on a destructive null-on-save hack that also wiped
manually entered Elo.
- Blended simulators buried their Elo/odds weight in module constants.
- Futures odds were not surfaced on the /admin/simulators inventory.
Changes:
- Add a configurable source policy: sourceEloPriority (ordered) and oddsWeight,
parsed/clamped in getSimulatorInputPolicy. resolveSourceElos/resolveRatings now
resolve each participant by the configured priority instead of a fixed order.
Futures-centric simulators (ncaam, ncaaw, world_cup, ncaa_football,
college_hockey) default to a futures-override priority.
- Drop the destructive nulling in batchSaveFuturesOddsForSimulator and
batchSaveSourceOdds; override is now governed by policy, preserving stored Elo.
- Thread an optional SimulationContext (oddsWeight) through the Simulator
interface so blended sims (UCL, World Cup, NCAA FB, MLB) read the blend weight
from the season policy; defaults preserve prior calibration when no context is
passed.
- Add a "Futures vs. Elo" strategy control and Odds Blend Weight input to the
Simulator Setup input-policy card, persisted via save-input-policy.
- Surface futures on /admin/simulators: a source badge and a Futures Odds quick
link; extend listSportsSeasonSimulatorSummaries with odds source info.
- Tests: configurable priority override (Elo/projections/rating), oddsWeight
parsing/clamping, prefersFuturesOdds, manifest defaults, an NCAA Football
context-blend behavioral test, and an updated non-destructive save test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhNeB7gN6VKene7sdbBVQT
2026-06-25 22:11:32 +00:00
|
|
|
defaultConfig: { ...BASE_CONFIG, bracketSize: 16, parityFactor: 850, inputPolicy: { sourceEloPriority: FUTURES_FIRST_PRIORITY } },
|
Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "worldRanking"],
|
|
|
|
|
derivableInputs: { sourceElo: ["sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "rankings", "futuresOdds", "bracket"],
|
|
|
|
|
},
|
|
|
|
|
brackt: {
|
|
|
|
|
defaultConfig: { iterations: 20_000 },
|
|
|
|
|
requiredInputs: [],
|
|
|
|
|
optionalInputs: [],
|
|
|
|
|
setupSections: ["participants"],
|
|
|
|
|
},
|
2026-05-12 14:22:34 -07:00
|
|
|
nll_bracket: {
|
|
|
|
|
defaultConfig: {
|
|
|
|
|
...BASE_CONFIG,
|
|
|
|
|
seasonGames: 18,
|
|
|
|
|
parityFactor: 400,
|
|
|
|
|
bracketSize: 8,
|
|
|
|
|
playoffTeams: 8,
|
|
|
|
|
regularSeasonTeamCount: 14,
|
|
|
|
|
homeFieldElo: 0,
|
|
|
|
|
regularSeasonMode: "project_remaining_games",
|
|
|
|
|
regularSeasonNoise: 0.9,
|
|
|
|
|
},
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["projectedWins", "sourceOdds", "seed"],
|
|
|
|
|
derivableInputs: { sourceElo: ["projectedWins", "sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "regularStandings", "bracket"],
|
|
|
|
|
},
|
Add MLS sport simulator (mls_bracket) (#422)
* Add MLS sport simulator (mls_bracket)
Adds Major League Soccer as a draftable sport with a full-season Monte Carlo
simulator. Models both preseason regular-season projection (34 games across
Eastern and Western conferences) and the MLS Cup Playoffs bracket, including
the Wild Card (single game + PKs), Round 1 best-of-3 series, Conference
Semis/Finals (single game), and MLS Cup.
P1–P8 mapping: MLS Cup winner, finalist, Conference Finals losers, Conference
Semifinals losers. Conference assignment reads from regularSeasonStandings.conference
or falls back to the region simulator input ("Eastern"/"Western").
Admin inputs: projectedTablePoints (primary, max 102 for 34×3), with derivation
chain to sourceElo via existing input-policy; sourceOdds as alternative.
No hardcoded team data — all inputs are admin-managed per season.
- database/schema.ts: add mls_bracket to simulatorTypeEnum
- drizzle/0104_chief_boom_boom.sql: migration for the new enum value
- mls-simulator.ts: MLSSimulator + exported pure helpers for testability
- registry.ts / manifest.ts / simulator-config.ts: register mls_bracket
- mls-simulator.test.ts: 38 unit tests covering all helpers and sync checks
https://claude.ai/code/session_015wkBJ3SYGcMGjsddKKGkwa
* Fix MLS simulator: config loading, sourceOdds fallback, normalization
Three issues found in code review comparing against EPL and NFL simulators:
1. Load simulator config from DB via getSportsSeasonSimulatorConfig so
admins can override iterations, seasonGames, parityFactor, drawRates
per season without code changes. Previously all constants were hardcoded.
2. Add sourceOdds → convertFuturesToElo fallback when no sourceElo is
present. EPL and NFL both do this; MLS was throwing immediately instead
of attempting the odds conversion that the manifest declares as optional.
3. Call normalizeSimulationResultColumns before returning results, matching
EPL's local normalization pattern for consistency (runner also normalizes
globally, but EPL calls it locally too).
https://claude.ai/code/session_015wkBJ3SYGcMGjsddKKGkwa
* Polish MLS simulator: configNumber zero, logger warning, Map lookup
Three small fixes from secondary code review:
1. configNumber: allow value >= 0 (not just > 0) so admins can
legitimately set baseDrawRate or drawDecay to 0 without the value
being silently discarded and replaced with the default.
2. Add logger.warn when a participant is excluded from simulation due
to a missing Elo rating, matching the NLL bracket-aware pattern.
Gives admins a visible signal instead of a silent exclusion.
3. getBySeeds: build a Map once instead of calling Array.find() per
seed. Eliminates 4M linear scans across 50k iterations for a 9-
element array — trivially fast either way, but Map is the right tool.
https://claude.ai/code/session_015wkBJ3SYGcMGjsddKKGkwa
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-14 12:45:53 -07:00
|
|
|
mls_bracket: {
|
|
|
|
|
defaultConfig: {
|
|
|
|
|
...BASE_CONFIG,
|
|
|
|
|
seasonGames: 34,
|
|
|
|
|
parityFactor: 400,
|
|
|
|
|
matchParityFactor: 400,
|
|
|
|
|
averageOpponentElo: 1500,
|
|
|
|
|
baseDrawRate: 0.26,
|
|
|
|
|
drawDecay: 0.002,
|
|
|
|
|
playoffTeamsPerConference: 9,
|
|
|
|
|
},
|
|
|
|
|
requiredInputs: ["sourceElo"],
|
|
|
|
|
optionalInputs: ["sourceOdds", "projectedTablePoints", "seed", "region"],
|
|
|
|
|
derivableInputs: { sourceElo: ["projectedTablePoints", "sourceOdds"] },
|
|
|
|
|
setupSections: ["participants", "eloRatings", "regularStandings"],
|
|
|
|
|
},
|
Formalize simulator system with manifest, input-policy, runner, and admin UI (#409)
Introduces three new schema tables (simulator_profiles,
sports_season_simulator_configs, season_participant_simulator_inputs),
a central model layer (app/models/simulator.ts), and a single runner
entry point so every simulator run follows the same prepare → simulate
→ persist → snapshot → recalculate flow.
Key additions:
- manifest.ts: per-simulator display names, default configs, required/
optional inputs, derivable-input declarations, and setup sections
- input-policy.ts: resolves sourceElo from projectedWins,
projectedTablePoints, or sourceOdds; resolves ratings from sourceOdds;
supports block / fallbackElo / averageKnown / worstKnownMinus strategies
- runner.ts: single entry point for admin simulation runs; materialises
derived inputs, normalises result columns, zeroes omitted participants,
snapshots EVs, and recalculates linked fantasy standings
- /admin/simulators: inventory page with per-season readiness and bulk run
- /admin/sports-seasons/:id/simulator: per-season setup page with readiness
summary, input-policy editor, raw JSON config override, and CSV bulk input
- NCAAM/NCAAW simulators now read ratings from season_participant_simulator_inputs,
falling back to the hardcoded name-keyed maps while DB data is being populated
- Clone flow copies simulator config by default; volatile inputs (odds, Elo)
only copied when explicitly requested
Code-review fixes included in this commit:
- source field in compatibility bridge checked with !== null instead of !== undefined
- sourceEloRequirementLabel no longer appends "configured fallback" when the
participant is already excluded from all resolved sources
- Duplicate inline label maps in input-policy.ts replaced with simulatorInputLabel
- save-config preserves existing inputPolicy when the submitted JSON omits it
- Input table truncation label added (Showing 20 of N)
- CSV description notes values must not contain commas
- N+1 comment added to listSportsSeasonSimulatorSummaries
- assertRegistrySchemaDriftFree called in manifest tests
- Runner test suite added covering happy path, already-running guard,
readiness failure, empty results, and error recovery with status reset
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:09:53 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const SIMULATOR_MANIFEST: Record<SimulatorType, SimulatorManifestProfile> =
|
|
|
|
|
Object.fromEntries(
|
|
|
|
|
SIMULATOR_TYPES.map((simulatorType) => {
|
|
|
|
|
const info = getSimulatorInfo(simulatorType);
|
|
|
|
|
const profile = PROFILES[simulatorType];
|
|
|
|
|
return [
|
|
|
|
|
simulatorType,
|
|
|
|
|
{
|
|
|
|
|
simulatorType,
|
|
|
|
|
displayName: info?.name ?? simulatorType,
|
|
|
|
|
description: info?.description ?? "",
|
|
|
|
|
...profile,
|
|
|
|
|
minParticipantInputs: profile.minParticipantInputs ?? 1,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
})
|
|
|
|
|
) as Record<SimulatorType, SimulatorManifestProfile>;
|
|
|
|
|
|
|
|
|
|
export function getManifestSimulatorProfile(
|
|
|
|
|
simulatorType: SimulatorType
|
|
|
|
|
): SimulatorManifestProfile {
|
|
|
|
|
return SIMULATOR_MANIFEST[simulatorType];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function simulatorInputLabel(key: SimulatorInputKey): string {
|
|
|
|
|
const labels: Record<SimulatorInputKey, string> = {
|
|
|
|
|
sourceOdds: "futures odds",
|
|
|
|
|
sourceElo: "Elo rating",
|
|
|
|
|
worldRanking: "ranking",
|
|
|
|
|
rating: "rating",
|
|
|
|
|
projectedWins: "projected wins",
|
|
|
|
|
projectedTablePoints: "projected table points",
|
|
|
|
|
seed: "seed",
|
|
|
|
|
region: "region",
|
|
|
|
|
metadata: "metadata",
|
|
|
|
|
};
|
|
|
|
|
return labels[key];
|
|
|
|
|
}
|