Score — Computing Rankings
The score phase reads signals from the database, applies a scoring preset, and writes ranked leaderboard entries to computed_scores. It makes zero GitHub API calls.
Entry point
scoreActivePresetForEntity() in lib/leaderboard/score.ts
Per-preset, per-period precompute
When scoring runs, it computes scores for all 6 time periods (today, week, month, quarter, half_year, all_time) × all active presets in a single pass. This is why switching presets is instant — every time period for every preset is already materialized.
The scoring function filters signals by date range for each period:
| Period | From | To |
|---|---|---|
today | 00:00:00 UTC today | now |
week | now − 7 days | now |
month | now − 30 days | now |
quarter | now − 90 days | now |
half_year | now − 180 days | now |
all_time | null (no filter) | null (no filter) |
all_time calls getSignalsForOrg() without a date filter — it returns every row in the signals table for the scope.
What gets stored
writePresetComputedScores() in lib/supabase/leaderboard-db.ts writes two things atomically:
leaderboard_materializations
One row per (preset_id, scope_type, organization_id, entity_type, time_period):
| Field | Purpose |
|---|---|
retention_state | current, superseded, expired, or purged |
version | Monotonic — incremented on each recompute |
row_count, signal_count | Metadata for cache freshness checks |
redis_cache_key | The cache key for Layer 3 (snapshot cache) |
signals_since, signals_until | The date range covered |
Old materializations are marked superseded rather than deleted — they stick around for the retention window (30 days) before purge_superseded_materializations() cleans them up. This function is orphaned — never called by application code. It must be invoked manually or via pg_cron.
computed_scores
One row per entity per materialization:
| Field | Purpose |
|---|---|
score | numeric(12,4) — the computed score |
rank | 1-based rank within the materialization |
breakdown | JSONB — per-signal-type score breakdown |
counts | JSONB — per-signal-type counts |
additions, deletions | Code volume (for contributor entries) |
member_count | Team size (for team entries) |
Entity identification uses polymorphic FK columns with a CHECK constraint:
(entity_type = 'contributor' AND user_id IS NOT NULL AND scored_team_id IS NULL) OR
(entity_type = 'team' AND scored_team_id IS NOT NULL AND user_id IS NULL) OR
(entity_type = 'repository' AND scored_repo_id IS NOT NULL AND user_id IS NULL)This is stronger than text-based polymorphism — the query planner can use per-column indexes.
Concurrent write protection
A Redis advisory lock (redisSetNx on leaderboard:write:{presetId}:{scope}:{entity}:{period}) prevents two concurrent scoring processes from writing interleaved rows to the same materialization. If the lock is held, the second process exits early — it does not retry.
Remaining entity types
After scoring the requested entity type (e.g., contributors), scoreRemainingEntityTypes() scores the other two (teams, repositories) in the background via after(). This keeps the initial API response fast while ensuring all entity views stay current.
Custom date range scoring
When a request includes from/to parameters, the scoring path bypasses computed_scores entirely. computeCustomDateRange() in lib/leaderboard/serve.ts:
- Calls
getSignalsForOrg()with the exact date range - Runs
computeScores()+buildLeaderboard()in-memory - Returns the results directly — nothing is written to the database
Repeated identical custom-range requests recompute each time, though the Redis response cache (Layer 2) may serve them from memory.