Files
Matt 7b407528f6 docs(01-04): complete auto-trigger plan — SUMMARY, STATE, ROADMAP updated
- 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
2026-02-27 01:08:26 +01:00

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
auto-trigger
notifications
ranking
evaluation
tRPC
RANK-09
RANK-10
requires provides affects
01-01
01-02
01-03
auto-trigger-on-evaluation-complete
retroactive-scan
admin-notifications
evaluation-submit-mutation
ranking-router
in-app-notifications
added patterns
fire-and-forget async
cooldown-guard
module-level-helper-function
created modified
src/server/services/in-app-notification.ts
src/server/routers/evaluation.ts
src/server/routers/ranking.ts
triggerAutoRankIfComplete defined as module-level (non-exported) function in evaluation.ts — avoids circular imports and keeps the auto-trigger logic colocated with the mutation it serves
EvaluationConfig null fallback typed as {} as EvaluationConfig rather than just {} — required for TypeScript strict mode to recognize rankingCriteria and autoRankOnComplete fields
ParsedRankingRule[] cast via unknown as Prisma.InputJsonValue — Prisma InputJsonValue does not overlap with typed arrays, double-cast is the correct pattern throughout the codebase
retroactiveScan uses RETROACTIVE triggerType to distinguish from MANUAL/AUTO/QUICK — prevents duplicate re-runs on subsequent scans
triggerAutoRank procedure in ranking.ts uses MANUAL triggerType (not AUTO) because it is admin-initiated, not system-initiated
duration completed_date tasks_completed files_modified
~8min 2026-02-27 2 3

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 + BarChart3 icon + normal priority
  • NotificationTypes.AI_RANKING_FAILED — type string + AlertTriangle icon + high priority

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:

  1. Count required assignments — skip if not all complete
  2. Read round.configJson → check autoRankOnComplete + rankingCriteria — skip silently if not configured
  3. Cooldown guard — skip if AUTO snapshot exists within last 5 minutes
  4. Call aiQuickRank() — executes in 10-30s asynchronously
  5. Create RankingSnapshot with triggerType: 'AUTO', triggeredById: null
  6. Notify admins via AI_RANKING_COMPLETE notification
  7. Catch-all: any error sends AI_RANKING_FAILED notification, 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_ACTIVE and ROUND_CLOSED rounds
  • Checks each for autoRankOnComplete + rankingCriteria config
  • Checks if all required assignments are complete
  • Skips rounds that already have a RETROACTIVE snapshot
  • 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 have rankingCriteria / autoRankOnComplete fields, 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.InputJsonValue produces TS2352 — neither type overlaps
  • Fix: Changed to result.parsedRules as unknown as Prisma.InputJsonValue (matching the pattern already used for rankedProjects arrays)
  • 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)
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, contains void triggerAutoRankIfComplete(...)
  • src/server/routers/ranking.ts — modified, contains triggerAutoRank and retroactiveScan
  • src/server/services/in-app-notification.ts — modified, contains AI_RANKING_COMPLETE and AI_RANKING_FAILED
  • Commits: 4683bb8 (Task 1), c310631 (Task 2)
  • Build: PASSED