diff --git a/app/models/__tests__/futures-odds-simulator.test.ts b/app/models/__tests__/futures-odds-simulator.test.ts index a2dfd63..c2c8342 100644 --- a/app/models/__tests__/futures-odds-simulator.test.ts +++ b/app/models/__tests__/futures-odds-simulator.test.ts @@ -69,6 +69,15 @@ describe("batchUpsertParticipantSimulatorInputs", () => { expect(sourceEloSql).toContain("coalesce"); const ratingSql = sqlToText(simulatorSet.rating).toLowerCase(); expect(ratingSql).toContain("coalesce"); + + // Metadata must drop the per-column method flag whenever that column gets a + // fresh direct value, so a stale "generated" flag can't hide a newly entered + // Elo/rating. Blindly preserving metadata (e.g. COALESCE) would be the bug. + const metadataSql = sqlToText(simulatorSet.metadata).toLowerCase(); + expect(metadataSql).toContain("sourceelomethod"); + expect(metadataSql).toContain("ratingmethod"); + expect(metadataSql).toContain("excluded.source_elo"); + expect(metadataSql).toContain("excluded.rating"); }); it("is a no-op when given no inputs", async () => { diff --git a/app/models/simulator.ts b/app/models/simulator.ts index 8e70274..b41469f 100644 --- a/app/models/simulator.ts +++ b/app/models/simulator.ts @@ -353,7 +353,20 @@ export async function batchUpsertParticipantSimulatorInputs( projectedTablePoints: sql`COALESCE(excluded.projected_table_points, ${schema.seasonParticipantSimulatorInputs.projectedTablePoints})`, seed: sql`COALESCE(excluded.seed, ${schema.seasonParticipantSimulatorInputs.seed})`, region: sql`COALESCE(excluded.region, ${schema.seasonParticipantSimulatorInputs.region})`, - metadata: sql`COALESCE(excluded.metadata, ${schema.seasonParticipantSimulatorInputs.metadata})`, + // Metadata carries the method flags (sourceEloMethod/ratingMethod) that + // tell readers whether the stored Elo/rating is generated vs. a trusted + // direct value. When a caller supplies explicit metadata, use it as-is + // (prepareSimulatorInputsForRun and the projection importer set the + // correct flags). Otherwise preserve existing metadata, but drop the + // method flag for any column receiving a fresh direct value — otherwise a + // stale "generated" flag would cause that newly-entered Elo/rating to be + // filtered out as derived (see getParticipantSimulatorInputs). + metadata: sql`CASE + WHEN excluded.metadata IS NOT NULL THEN excluded.metadata + ELSE COALESCE(${schema.seasonParticipantSimulatorInputs.metadata}, '{}'::jsonb) + - (CASE WHEN excluded.source_elo IS NOT NULL THEN 'sourceEloMethod' ELSE '' END) + - (CASE WHEN excluded.rating IS NOT NULL THEN 'ratingMethod' ELSE '' END) + END`, updatedAt: sql`excluded.updated_at`, }, }); diff --git a/app/routes/admin.sports-seasons.$id.simulator.tsx b/app/routes/admin.sports-seasons.$id.simulator.tsx index ad08195..578315c 100644 --- a/app/routes/admin.sports-seasons.$id.simulator.tsx +++ b/app/routes/admin.sports-seasons.$id.simulator.tsx @@ -280,14 +280,21 @@ export async function action({ request, params }: Route.ActionArgs): Promise