fix: advance-vote toggle off restores score-only ranking
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m52s
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:
@@ -226,17 +226,16 @@ function SortableProjectRow({
|
|||||||
)
|
)
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
{/* Active pass rate chip */}
|
{/* Active pass rate chip — only when advance votes factor into rank */}
|
||||||
{(() => {
|
{useBalancedPassRate && (() => {
|
||||||
const active = useBalancedPassRate && balancedPassRate != null ? balancedPassRate : rawPassRate
|
const active = balancedPassRate ?? rawPassRate
|
||||||
if (active == null) return null
|
if (active == null) return null
|
||||||
const label = useBalancedPassRate && balancedPassRate != null ? 'Bal Yes%' : 'Yes%'
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className="inline-flex items-baseline gap-1 rounded-md border bg-muted/50 px-2 py-0.5 text-xs tabular-nums"
|
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 className="font-semibold">{Math.round(active * 100)}%</span>
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
@@ -432,18 +431,17 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
const dedupedStartup = dedup(startup)
|
const dedupedStartup = dedup(startup)
|
||||||
const dedupedConcept = dedup(concept)
|
const dedupedConcept = dedup(concept)
|
||||||
|
|
||||||
// Composite ranking score combining (balanced-or-raw) average with the
|
// Composite ranking score. When the advance-vote toggle is OFF, pass
|
||||||
// (balanced-or-raw) advance pass rate via the round's scoreWeight /
|
// rate is not in the formula at all — pure score-based ranking. When ON,
|
||||||
// passRateWeight sliders. Same formula used by the live re-sort effect
|
// the harshness-corrected (balanced) pass rate enters at passRateWeight.
|
||||||
// and the threshold cutoff so all three stay in lock-step.
|
// 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 compositeFor = (projectId: string, rawScoreFallback: number | null | undefined): number => {
|
||||||
const b = evalScores.balanced[projectId]
|
const b = evalScores.balanced[projectId]
|
||||||
const score = useBalanced && b?.balancedAverage != null ? b.balancedAverage : (rawScoreFallback ?? null)
|
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 scoreUnit = score != null ? Math.max(0, Math.min(1, (score - 1) / 9)) : 0
|
||||||
const passRate =
|
if (!useBalancedPassRate) return scoreUnit
|
||||||
useBalancedPassRate && b?.balancedPassRate != null ? b.balancedPassRate
|
const passRate = b?.balancedPassRate ?? b?.rawPassRate ?? null
|
||||||
: b?.rawPassRate != null ? b.rawPassRate
|
|
||||||
: null
|
|
||||||
const passUnit = passRate ?? 0
|
const passUnit = passRate ?? 0
|
||||||
const sW = localScoreWeight
|
const sW = localScoreWeight
|
||||||
const pW = localPassRateWeight
|
const pW = localPassRateWeight
|
||||||
@@ -529,10 +527,8 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
const b = evalScores.balanced[projectId]
|
const b = evalScores.balanced[projectId]
|
||||||
const score = useBalanced && b?.balancedAverage != null ? b.balancedAverage : (rawScoreFallback ?? null)
|
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 scoreUnit = score != null ? Math.max(0, Math.min(1, (score - 1) / 9)) : 0
|
||||||
const passRate =
|
if (!useBalancedPassRate) return scoreUnit
|
||||||
useBalancedPassRate && b?.balancedPassRate != null ? b.balancedPassRate
|
const passRate = b?.balancedPassRate ?? b?.rawPassRate ?? null
|
||||||
: b?.rawPassRate != null ? b.rawPassRate
|
|
||||||
: null
|
|
||||||
const passUnit = passRate ?? 0
|
const passUnit = passRate ?? 0
|
||||||
const sW = localScoreWeight
|
const sW = localScoreWeight
|
||||||
const pW = localPassRateWeight
|
const pW = localPassRateWeight
|
||||||
@@ -985,19 +981,16 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
const threshold = evalConfig?.advanceScoreThreshold ?? 0
|
const threshold = evalConfig?.advanceScoreThreshold ?? 0
|
||||||
|
|
||||||
// Effective ranking score for the threshold cutoff. Mirrors
|
// Effective ranking score for the threshold cutoff. Mirrors
|
||||||
// the composite formula used by the sort: weighted blend of
|
// the composite formula used by the sort. When the advance-vote
|
||||||
// (balanced-or-raw) avg score and (balanced-or-raw) pass rate.
|
// toggle is OFF, threshold compares against the score directly
|
||||||
// For the visible 1-10 threshold we render the score component
|
// (back in 1-10 space) so the cutoff matches admin intent.
|
||||||
// back on the 1-10 scale.
|
|
||||||
const effectiveScore = (id: string) => {
|
const effectiveScore = (id: string) => {
|
||||||
const e = rankingMap.get(id)
|
const e = rankingMap.get(id)
|
||||||
const b = evalScores?.balanced[id]
|
const b = evalScores?.balanced[id]
|
||||||
const score = useBalanced && b?.balancedAverage != null ? b.balancedAverage : (e?.avgGlobalScore ?? null)
|
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 scoreUnit = score != null ? Math.max(0, Math.min(1, (score - 1) / 9)) : 0
|
||||||
const passRate =
|
const passRate = b?.balancedPassRate ?? b?.rawPassRate ?? null
|
||||||
useBalancedPassRate && b?.balancedPassRate != null ? b.balancedPassRate
|
|
||||||
: b?.rawPassRate != null ? b.rawPassRate
|
|
||||||
: null
|
|
||||||
const passUnit = passRate ?? 0
|
const passUnit = passRate ?? 0
|
||||||
const sW = localScoreWeight
|
const sW = localScoreWeight
|
||||||
const pW = localPassRateWeight
|
const pW = localPassRateWeight
|
||||||
@@ -1158,9 +1151,10 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between rounded-lg border p-3">
|
<div className="flex items-center justify-between rounded-lg border p-3">
|
||||||
<div className="flex flex-col">
|
<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">
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Switch checked={useBalancedPassRate} onCheckedChange={persistUseBalancedPassRate} />
|
<Switch checked={useBalancedPassRate} onCheckedChange={persistUseBalancedPassRate} />
|
||||||
|
|||||||
Reference in New Issue
Block a user