fix: sort ranking display by avgGlobalScore, not compositeScore
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m33s

The display was sorted by compositeScore (which factors in pass rate and
z-score normalization) but the cutoff line and displayed values use
avgGlobalScore. This caused projects with high averages but low pass
rates (e.g. 1/2 Yes) to appear below the cutoff even when their average
exceeded the threshold.

Now sorts by avgGlobalScore (the visible metric) with compositeScore as
tiebreaker. Also adds a green left border to advancing projects for
clearer threshold highlighting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-02 21:20:11 +01:00
parent 761a203063
commit 8c5f4998a8

View File

@@ -425,10 +425,14 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
const dedupedStartup = dedup(startup) const dedupedStartup = dedup(startup)
const dedupedConcept = dedup(concept) const dedupedConcept = dedup(concept)
// Sort by compositeScore descending — ensures display order matches the formula // Sort by avgGlobalScore descending (the metric displayed to the admin), with
// regardless of how the snapshot was originally generated (AI vs formula). // compositeScore as tiebreaker. This ensures the visible ordering matches the
dedupedStartup.sort((a, b) => b.compositeScore - a.compositeScore) // numbers on screen AND the threshold cutoff line lands correctly (it checks
dedupedConcept.sort((a, b) => b.compositeScore - a.compositeScore) // avgGlobalScore, so the list must be sorted by that same metric).
dedupedStartup.sort((a, b) =>
(b.avgGlobalScore ?? 0) - (a.avgGlobalScore ?? 0) || b.compositeScore - a.compositeScore)
dedupedConcept.sort((a, b) =>
(b.avgGlobalScore ?? 0) - (a.avgGlobalScore ?? 0) || b.compositeScore - a.compositeScore)
// Track original order for override detection (same effect = always in sync) // Track original order for override detection (same effect = always in sync)
const order: Record<string, number> = {} const order: Record<string, number> = {}
@@ -944,7 +948,7 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
let cutoffIndex = -1 let cutoffIndex = -1
if (isThresholdMode) { if (isThresholdMode) {
// Find the FIRST project that does NOT meet the threshold — cutoff goes before it. // Find the FIRST project that does NOT meet the threshold — cutoff goes before it.
// Works correctly when list is sorted by compositeScore (which correlates with avgGlobalScore). // Works correctly because localOrder is sorted by avgGlobalScore (the same metric).
const firstFailIdx = localOrder[category].findIndex((id) => { const firstFailIdx = localOrder[category].findIndex((id) => {
const e = rankingMap.get(id) const e = rankingMap.get(id)
return (e?.avgGlobalScore ?? 0) < threshold return (e?.avgGlobalScore ?? 0) < threshold
@@ -993,7 +997,9 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }} exit={{ opacity: 0, y: -20 }}
className={isAdvancing ? 'rounded-lg bg-emerald-50 dark:bg-emerald-950/20' : ''} className={isAdvancing
? 'rounded-lg bg-emerald-50 border-l-4 border-emerald-400 dark:bg-emerald-950/20 dark:border-emerald-600'
: ''}
> >
<SortableProjectRow <SortableProjectRow
projectId={projectId} projectId={projectId}