From 900700f6ae74b70fa21a11ffb6d4bcd32bb4a309 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 27 Apr 2026 14:45:44 +0200 Subject: [PATCH] fix: advance-vote toggle off restores score-only ranking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The toggle was previously a balanced-vs-raw switch for the pass-rate input, but pass rate was always present in the composite formula. That contradicted the original ask — admins want a clean way to say 'don't factor in yes/no at all'. Toggle off now drops pass rate from the composite entirely; the ranking falls back to pure balanced-or-raw score, matching the behavior before this thread of work introduced the composite formula. The label is updated accordingly ('Factor advance votes into ranking'), and the pass-rate chip on each list row only appears when the toggle is on so admins aren't shown a number that isn't influencing rank. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../admin/round/ranking-dashboard.tsx | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/components/admin/round/ranking-dashboard.tsx b/src/components/admin/round/ranking-dashboard.tsx index 46789dd..ac42464 100644 --- a/src/components/admin/round/ranking-dashboard.tsx +++ b/src/components/admin/round/ranking-dashboard.tsx @@ -226,17 +226,16 @@ function SortableProjectRow({ ) })()} - {/* Active pass rate chip */} - {(() => { - const active = useBalancedPassRate && balancedPassRate != null ? balancedPassRate : rawPassRate + {/* Active pass rate chip — only when advance votes factor into rank */} + {useBalancedPassRate && (() => { + const active = balancedPassRate ?? rawPassRate if (active == null) return null - const label = useBalancedPassRate && balancedPassRate != null ? 'Bal Yes%' : 'Yes%' return ( - {label} + Yes% {Math.round(active * 100)}% ) @@ -432,18 +431,17 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran const dedupedStartup = dedup(startup) const dedupedConcept = dedup(concept) - // Composite ranking score combining (balanced-or-raw) average with the - // (balanced-or-raw) advance pass rate via the round's scoreWeight / - // passRateWeight sliders. Same formula used by the live re-sort effect - // and the threshold cutoff so all three stay in lock-step. + // Composite ranking score. When the advance-vote toggle is OFF, pass + // rate is not in the formula at all — pure score-based ranking. When ON, + // the harshness-corrected (balanced) pass rate enters at passRateWeight. + // The score toggle independently picks balanced vs raw average. Same + // formula used by the live re-sort effect and the threshold cutoff. const compositeFor = (projectId: string, rawScoreFallback: number | null | undefined): number => { const b = evalScores.balanced[projectId] const score = useBalanced && b?.balancedAverage != null ? b.balancedAverage : (rawScoreFallback ?? null) const scoreUnit = score != null ? Math.max(0, Math.min(1, (score - 1) / 9)) : 0 - const passRate = - useBalancedPassRate && b?.balancedPassRate != null ? b.balancedPassRate - : b?.rawPassRate != null ? b.rawPassRate - : null + if (!useBalancedPassRate) return scoreUnit + const passRate = b?.balancedPassRate ?? b?.rawPassRate ?? null const passUnit = passRate ?? 0 const sW = localScoreWeight const pW = localPassRateWeight @@ -529,10 +527,8 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran const b = evalScores.balanced[projectId] const score = useBalanced && b?.balancedAverage != null ? b.balancedAverage : (rawScoreFallback ?? null) const scoreUnit = score != null ? Math.max(0, Math.min(1, (score - 1) / 9)) : 0 - const passRate = - useBalancedPassRate && b?.balancedPassRate != null ? b.balancedPassRate - : b?.rawPassRate != null ? b.rawPassRate - : null + if (!useBalancedPassRate) return scoreUnit + const passRate = b?.balancedPassRate ?? b?.rawPassRate ?? null const passUnit = passRate ?? 0 const sW = localScoreWeight const pW = localPassRateWeight @@ -985,19 +981,16 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran const threshold = evalConfig?.advanceScoreThreshold ?? 0 // Effective ranking score for the threshold cutoff. Mirrors - // the composite formula used by the sort: weighted blend of - // (balanced-or-raw) avg score and (balanced-or-raw) pass rate. - // For the visible 1-10 threshold we render the score component - // back on the 1-10 scale. + // the composite formula used by the sort. When the advance-vote + // toggle is OFF, threshold compares against the score directly + // (back in 1-10 space) so the cutoff matches admin intent. const effectiveScore = (id: string) => { const e = rankingMap.get(id) const b = evalScores?.balanced[id] const score = useBalanced && b?.balancedAverage != null ? b.balancedAverage : (e?.avgGlobalScore ?? null) + if (!useBalancedPassRate) return score ?? 0 const scoreUnit = score != null ? Math.max(0, Math.min(1, (score - 1) / 9)) : 0 - const passRate = - useBalancedPassRate && b?.balancedPassRate != null ? b.balancedPassRate - : b?.rawPassRate != null ? b.rawPassRate - : null + const passRate = b?.balancedPassRate ?? b?.rawPassRate ?? null const passUnit = passRate ?? 0 const sW = localScoreWeight const pW = localPassRateWeight @@ -1158,9 +1151,10 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
- Balance juror approval rate (advance vote) + Factor advance votes into ranking - Weights yes/no votes by how often each juror says yes. Off uses raw pass rate. + When on, yes/no votes (harshness-corrected) blend with the score via the + score/pass-rate weights. When off, the ranking is score-only.