feat(surface-elo): mirror batchUpsert writes to canonical table

The simulator now reads surface Elo from the canonical table
(participant_surface_elos). The existing per-window admin page still
posts to batchUpsertSurfaceElos; without this change, those edits
would silently fail to affect simulator output. Mirror every write
to both tables until Phase 4 removes the per-window path entirely.

Phase 3 Task 5 (partial — canonical write path; dedicated canonical
admin UI deferred; existing page continues to work and now
edits both tables).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Chris Parsons 2026-05-01 22:23:44 +00:00
parent 3e81dfe8da
commit 7483cd628b
No known key found for this signature in database

View file

@ -8,7 +8,7 @@
import { database } from "~/database/context";
import { participantSurfaceElos, seasonParticipantSurfaceElos, seasonParticipants } from "~/database/schema";
import { eq, sql } from "drizzle-orm";
import { eq, inArray, sql } from "drizzle-orm";
export type CourtSurface = "hard" | "clay" | "grass";
@ -99,6 +99,45 @@ export async function batchUpsertSurfaceElos(
updatedAt: sql`excluded.updated_at`,
},
});
// Mirror writes to the canonical participant_surface_elos table.
// The simulator reads from canonical, so per-window-only edits would
// not affect simulations. Remove the per-window path entirely in Phase 4.
const sps = await db
.select({ id: seasonParticipants.id, canonicalId: seasonParticipants.participantId })
.from(seasonParticipants)
.where(inArray(seasonParticipants.id, inputs.map((i) => i.participantId)));
const canonicalByInputId = new Map(sps.map((sp) => [sp.id, sp.canonicalId]));
const canonicalRows = inputs
.map((i) => ({
canonicalId: canonicalByInputId.get(i.participantId),
worldRanking: i.worldRanking ?? null,
eloHard: i.eloHard ?? null,
eloClay: i.eloClay ?? null,
eloGrass: i.eloGrass ?? null,
}))
.filter((r): r is typeof r & { canonicalId: string } => typeof r.canonicalId === "string");
if (canonicalRows.length === 0) return;
await db
.insert(participantSurfaceElos)
.values(canonicalRows.map(({ canonicalId, ...rest }) => ({
participantId: canonicalId,
...rest,
updatedAt: now,
})))
.onConflictDoUpdate({
target: [participantSurfaceElos.participantId],
set: {
worldRanking: sql`excluded.world_ranking`,
eloHard: sql`excluded.elo_hard`,
eloClay: sql`excluded.elo_clay`,
eloGrass: sql`excluded.elo_grass`,
updatedAt: sql`excluded.updated_at`,
},
});
}
/**