feat: factor balanced pass rate into composite rankings

The dashboard now computes its own composite ranking score on the
client, blending (balanced-or-raw) average score with (balanced-or-raw)
advance pass rate via the existing scoreWeight / passRateWeight
sliders. Both inputs are toggled independently:

- 'Balance juror grading style (score)' — existing useBalancedRanking
- 'Balance juror approval rate (advance vote)' — new useBalancedPassRate

Both default to true and persist per-round. The pass rate is balanced
the same way scores are: each juror's personal yes-rate gives them a
Bernoulli stddev, each vote is z-normalized against that, and the
project's mean z is rescaled to the round's overall yes rate. A 'yes'
from a juror who rarely says yes counts more than a 'yes' from a
lenient juror.

List rows now show two chips — score (Bal/Raw X.XX) and pass rate
(Bal Yes% / Yes% N%) — so admins can see what's driving the order.
The threshold cutoff and live re-sort effect both use the same
composite formula.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-04-27 14:28:49 +02:00
parent aed5e078b3
commit 70f1f64ea3
4 changed files with 268 additions and 36 deletions

View File

@@ -147,6 +147,12 @@ export const EvaluationConfigSchema = z.object({
// from the dashboard side panel.
useBalancedRanking: z.boolean().default(true),
// Whether the project pass rate (yes/no advance vote) is harshness-corrected
// before being fed into the composite ranking formula. When true, a "yes" from
// a juror who rarely says yes weighs more than a "yes" from a lenient juror.
// Toggled separately from useBalancedRanking; both default to true.
useBalancedPassRate: z.boolean().default(true),
// Ranking (Phase 1)
rankingEnabled: z.boolean().default(false),
rankingCriteria: z.string().optional(),