Futures odds entry was split across two unrelated places: a standalone
/futures-odds page (which auto-ran the simulation on save and surfaced a
"Simulator is not ready" run failure as if the save itself had failed) and
the Bulk Simulator Inputs CSV importer on the simulator setup page.
Consolidate everything onto the Bulk Simulator Inputs card:
- batchUpsertParticipantSimulatorInputs now COALESCE-wraps every conflict
update column, so a partial paste (e.g. odds-only) updates just the columns
it provides instead of nulling out previously stored Elo/rating/etc.
- The bulk importer accepts sportsbook-style paste (one team per line ending
in American odds) when no CSV header is present, reusing the existing fuzzy
matcher, and auto-runs the simulation on save. A not-ready run is reported
as a successful save plus the readiness gap, never as a failed save.
- Retire the standalone /futures-odds page (now redirects to the simulator
setup page) and drop the redundant Futures links.
- Remove the now-dead futures-only model paths (batchSaveSourceOdds,
clearSourceOddsForParticipants, batchSaveFuturesOddsForSimulator,
batchSaveParticipantSimulatorSourceOdds); the EV-table bridge is preserved
by batchUpsertParticipantSimulatorInputs.
- Repoint the test to the consolidated path and assert the non-destructive
COALESCE behaviour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BHnoQ7myY3PzNdTnd7iGNE
Co-authored-by: Claude <noreply@anthropic.com>
Reviewed-on: #117
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>