fix: advance-vote toggle off restores score-only ranking
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m52s

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) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-04-27 14:45:44 +02:00
parent e0103fa956
commit 900700f6ae

View File

@@ -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 (
<span
className="inline-flex items-baseline gap-1 rounded-md border bg-muted/50 px-2 py-0.5 text-xs tabular-nums"
title={`${useBalancedPassRate && balancedPassRate != null ? 'Harshness-corrected approval rate' : 'Raw approval rate'} (factored into rank)`}
title="Harshness-corrected approval rate (factored into rank)"
>
<span className="text-[10px] uppercase tracking-wide text-muted-foreground">{label}</span>
<span className="text-[10px] uppercase tracking-wide text-muted-foreground">Yes%</span>
<span className="font-semibold">{Math.round(active * 100)}%</span>
</span>
)
@@ -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
</div>
<div className="flex items-center justify-between rounded-lg border p-3">
<div className="flex flex-col">
<span className="text-sm font-medium">Balance juror approval rate (advance vote)</span>
<span className="text-sm font-medium">Factor advance votes into ranking</span>
<span className="text-xs text-muted-foreground">
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.
</span>
</div>
<Switch checked={useBalancedPassRate} onCheckedChange={persistUseBalancedPassRate} />