fix: ranking sorted by composite score, deduplicate AI results, single cutoff line
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m0s
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m0s
- Sort all ranked projects by compositeScore descending so highest-rated projects always appear first (instead of relying on AI's inconsistent rank order) - Deduplicate AI ranking response (AI sometimes returns same project multiple times) - Deduplicate ranking entries and reorder IDs on dashboard load as defensive measure - Show advancement cutoff line only once (precompute last advancing index) - Override badge only shown when admin has actually drag-reordered (not on fresh rankings) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -510,6 +510,14 @@ export async function executeAIRanking(
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Failed to parse ranking response as JSON' })
|
||||
}
|
||||
|
||||
// Deduplicate AI response — keep only the first occurrence of each project_id
|
||||
const seenAnonIds = new Set<string>()
|
||||
aiRanked = aiRanked.filter((entry) => {
|
||||
if (seenAnonIds.has(entry.project_id)) return false
|
||||
seenAnonIds.add(entry.project_id)
|
||||
return true
|
||||
})
|
||||
|
||||
// Build a lookup by anonymousId for project data
|
||||
const projectByAnonId = new Map(
|
||||
anonymized.map((a) => [a.project_id, projects.find((p) => p.id === idMap.get(a.project_id))!])
|
||||
@@ -548,13 +556,17 @@ export async function executeAIRanking(
|
||||
}))
|
||||
.sort((a, b) => b.compositeScore - a.compositeScore)
|
||||
|
||||
let nextRank = rankedProjects.length + 1
|
||||
for (const proj of unrankedProjects) {
|
||||
proj.rank = nextRank++
|
||||
rankedProjects.push(proj)
|
||||
}
|
||||
|
||||
// Re-normalize ranks to be contiguous (1, 2, 3, …)
|
||||
// Sort ALL projects by compositeScore descending (deterministic, score-based order).
|
||||
// The AI's rank is advisory — the computed composite score (which already incorporates
|
||||
// weighted criteria, z-score normalization, pass rate, and evaluator count) is the
|
||||
// authoritative sort key so that highest-rated projects always appear first.
|
||||
rankedProjects.sort((a, b) => b.compositeScore - a.compositeScore)
|
||||
|
||||
// Re-number ranks to be contiguous (1, 2, 3, …)
|
||||
rankedProjects.forEach((p, i) => { p.rank = i + 1 })
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user