- 01-04-SUMMARY.md: full plan summary with 7 procedure list, deviations, build status - STATE.md: plan 4/4 complete, decisions recorded, session updated - ROADMAP.md: Phase 1 all 4 plans complete - REQUIREMENTS.md: RANK-09 and RANK-10 marked complete
6.5 KiB
phase, plan, subsystem, tags, requirements, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | requirements | dependency_graph | tech_stack | key_files | decisions | metrics | |||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-ai-ranking-backend | 04 | ai-ranking |
|
|
|
|
|
|
|
Phase 1 Plan 04: Auto-Trigger + Retroactive Scan Summary
One-liner: Fire-and-forget auto-ranking on evaluation completion with 5-minute cooldown guard, plus retroactive scan procedure for rounds already complete at deploy time.
What Was Built
Task 1: AI_RANKING_COMPLETE + AI_RANKING_FAILED notification types
Added two new entries to src/server/services/in-app-notification.ts:
NotificationTypes.AI_RANKING_COMPLETE— type string +BarChart3icon +normalpriorityNotificationTypes.AI_RANKING_FAILED— type string +AlertTriangleicon +highpriority
Pattern follows existing FILTERING_COMPLETE / FILTERING_FAILED entries exactly.
Task 2: Auto-trigger hook + new ranking procedures
evaluation.ts — triggerAutoRankIfComplete function:
async function triggerAutoRankIfComplete(
roundId: string,
prisma: PrismaClient,
userId: string,
): Promise<void>
Logic flow:
- Count required assignments — skip if not all complete
- Read
round.configJson→ checkautoRankOnComplete+rankingCriteria— skip silently if not configured - Cooldown guard — skip if AUTO snapshot exists within last 5 minutes
- Call
aiQuickRank()— executes in 10-30s asynchronously - Create
RankingSnapshotwithtriggerType: 'AUTO',triggeredById: null - Notify admins via
AI_RANKING_COMPLETEnotification - Catch-all: any error sends
AI_RANKING_FAILEDnotification, never rethrows
Fire-and-forget call in submit mutation (line 378):
void triggerAutoRankIfComplete(evaluation.assignment.roundId, ctx.prisma, ctx.user.id)
Placed after $transaction([evaluation.update, assignment.update]), before logAudit. The submission returns immediately — ranking runs asynchronously.
ranking.ts — 7 total procedures:
| Procedure | Type | Trigger | Description |
|---|---|---|---|
parseRankingCriteria |
mutation | admin | Preview-only parse (RANK-01, RANK-03) |
executeRanking |
mutation | admin | Confirmed ranking with pre-parsed rules (RANK-05, RANK-06, RANK-08) |
quickRank |
mutation | admin | Parse + execute in one step (RANK-04) |
listSnapshots |
query | admin | List snapshots for round, most recent first |
getSnapshot |
query | admin | Retrieve single snapshot by ID |
triggerAutoRank |
mutation | admin | Manual trigger from round config (RANK-09) |
retroactiveScan |
mutation | admin | Scan all active/closed rounds (RANK-10) |
retroactiveScan logic:
- Finds all
ROUND_ACTIVEandROUND_CLOSEDrounds - Checks each for
autoRankOnComplete + rankingCriteriaconfig - Checks if all required assignments are complete
- Skips rounds that already have a
RETROACTIVEsnapshot - Executes ranking sequentially (not parallel) to avoid OpenAI rate limits
- Returns
{ results[], total, triggered }summary
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] TypeScript strict mode: ?? {} loses EvaluationConfig type
- Found during: Task 2 typecheck
- Issue:
(configJson as EvaluationConfig | null) ?? {}— the{}fallback widens the type to{}which doesn't haverankingCriteria/autoRankOnCompletefields, causing TS2339 errors - Fix: Changed to
?? ({} as EvaluationConfig)in all three locations (evaluation.ts + two in ranking.ts) - Files modified:
src/server/routers/evaluation.ts,src/server/routers/ranking.ts - Commit:
c310631
2. [Rule 1 - Bug] TypeScript: ParsedRankingRule[] requires double-cast to InputJsonValue
- Found during: Task 2 typecheck
- Issue:
result.parsedRules as Prisma.InputJsonValueproduces TS2352 — neither type overlaps - Fix: Changed to
result.parsedRules as unknown as Prisma.InputJsonValue(matching the pattern already used forrankedProjectsarrays) - Files modified:
src/server/routers/ranking.ts(triggerAutoRank + retroactiveScan) - Commit:
c310631
Build Status
npm run typecheck— PASSED (0 errors)npm run build— PASSED (full production build)
Key Links Implemented
| From | To | Via |
|---|---|---|
evaluation.ts submit |
triggerAutoRankIfComplete |
void fire-and-forget after isCompleted: true |
triggerAutoRankIfComplete |
ai-ranking.ts quickRank |
aiQuickRank(criteriaText, roundId, prisma, userId) |
triggerAutoRankIfComplete |
in-app-notification.ts |
notifyAdmins({ type: AI_RANKING_COMPLETE }) |
ranking.ts triggerAutoRank |
ai-ranking.ts quickRank |
admin-initiated, creates MANUAL snapshot |
ranking.ts retroactiveScan |
ai-ranking.ts quickRank |
sequential per-round, creates RETROACTIVE snapshot |
Self-Check: PASSED
src/server/routers/evaluation.ts— modified, containsvoid triggerAutoRankIfComplete(...)src/server/routers/ranking.ts— modified, containstriggerAutoRankandretroactiveScansrc/server/services/in-app-notification.ts— modified, containsAI_RANKING_COMPLETEandAI_RANKING_FAILED- Commits:
4683bb8(Task 1),c310631(Task 2) - Build: PASSED