You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TypeScript Development-track ratings engine ported and integrated
✅ Done
Phase 4
Marathon Match ratings engine with relative-scoring support
✅ Done
Phase 5
autopilot-v6 calls /stats/refresh and /stats/rerate at challenge end
✅ Done
Phase 6
reports-api-v6 SQL migrated to unified tables
✅ Done
Phase 7
Parity validation and consumer cutover (this phase)
🔄 In progress
New M2M Scopes
Scope
Route
Purpose
refresh:member_stats
POST /members/:handle/stats/refresh
Recompute aggregate stats from challenge results
rerate:member_stats
POST /members/:handle/stats/rerate
Re-run ratings from a given challenge forward
Configuration Flag
STATS_READ_SOURCE controls which stats tables back the read path.
unified is the default and reads from memberStats plus memberStatsHistory.
legacy falls back to the pre-migration table set during staged rollout or rollback validation.
Recommended rollout:
Backfill unified tables.
Run parity checks while reads stay on legacy.
Switch reads to unified after parity is clean and downstream consumers are verified.
Remove the flag only after the rollback window closes and legacy tables are no longer needed operationally.
Operational Runbook
1. Pre-flight checklist
Confirm all three database env vars are set: DATABASE_URL, CHALLENGES_DB_URL, REVIEW_DB_URL
Confirm REVIEW_DB_URL points to the review-api database that contains challengeResult; this is separate from the member Prisma schema deployment
Confirm the challenge-api-v6 migration that adds hidden legacy challenge types has been deployed so legacy subtracks such as ARCHITECTURE, ASSEMBLY_COMPETITION, and SRM can be resolved during stats backfill
Confirm STATS_READ_SOURCE=legacy is set so reads stay on legacy tables during backfill
Confirm member-api-v6 is running and /members/health returns 200
2. Phase A - Full backfill (run once, idempotent)
Step
Command
Notes
A1
node src/scripts/recalculateMemberStats.js
Full backfill of all users; writes memberStats + memberStatsHistory; starts aggregate rows from legacy stats tables when present, supplements them with newer review-api challengeResult rows, falls back to review-api challengeResult or ChallengeWinner when legacy rows do not exist, and also supplements memberStatsHistory with completed review/winner-backed challenges that were never written to the legacy history tables. Preserves existing rating/rank fields, processes users with bounded parallelism (--concurrency, default 4), and checkpoints processed user IDs to ./recalculateMemberStats.processedUserIds.json after each completed batch
Recommended for the initial full load when total runtime matters; still backfills legacy rating/rank fields but skips the expensive Development rerate replay so rerates can be run separately in Phase D
Re-run aggregate stats only if history is already seeded; full-user runs replace stale public unified-only rows for migrated members while leaving legacy-backed parents intact
If the script exits before processing users with REVIEW_DB_URL does not expose challengeResult, the configured review database is missing the review-api table. Deploy review-api-v6 migrations or update REVIEW_DB_URL to the correct database before retrying.
For large environments, prefer --skip-rerate during the bulk backfill and run the Development rerate pass separately afterward. The rerate replay is usually the longest phase by a wide margin because it replays rated challenge history one challenge at a time.
The script logs per-batch timing breakdowns for preload queries, aggregate generation, stats/history writes, rerates, and checkpoint writes, plus slow-user samples for the aggregate/history/rerate phases.
3. Phase B - Parity validation (run before switching read source)