Module map
| Module | Entry function | Role |
|---|---|---|
lib/leaderboard/ingest.ts | runIngest(ctx) | Fetch GitHub data, normalize, store signals |
lib/leaderboard/score.ts | scoreActivePresetForEntity(ctx, repos, teamMemberships, entityType) | Read signals, compute scores for all 6 periods, write to DB |
lib/leaderboard/serve.ts | serveLeaderboard(ctx) | Dispatch to precomputed or custom-range path |
lib/leaderboard/ingest-freshness.ts | evaluateIngestFreshness(lastSuccessfulIngestAt, filters) | Cooldown + TTL logic |
lib/scoring/engine.ts | computeSignalScore(), computeScores() | Per-signal algorithm, contributor aggregation |
lib/scoring/aggregate.ts | aggregateLeaderboard(), aggregateByRepository(), aggregateByTeamSignals() | Dispatcher, repo aggregation, team aggregation |
lib/scoring/normalize.ts | normalizeGitHubData(rawData) | Raw GitHub → Signal[], content hash, bot detection |
lib/scoring/rules.ts | — (constants only) | Default scoring ruleset values |
lib/constants/time.ts | — (constants only) | SCORE_TIME_PERIODS, INGEST.TTL_MS, INGEST.COOLDOWN_MS |
lib/github/ingest-limits.ts | — (constants only) | Per-repo fetch limits and concurrency |
lib/supabase/signals.ts | getSignalsForOrg(), upsertSignals() | Signal reads and writes |
lib/supabase/leaderboard-db.ts | writePresetComputedScores(), getPresetComputedScores(), preset CRUD | Materialization + preset persistence |
lib/supabase/ingest-state.ts | getLastSuccessfulIngestAt(), setLastSuccessfulIngestAt() | Ingest log reads and writes |
Correct function names
The following names have changed from earlier versions. Always use the names in the right column:
| Old (incorrect) | Correct |
|---|---|
fetchRepoActivityGraphQL | fetchOrgScoringDataGraphQL |
getSignalsForScoring | getSignalsForOrg |
writeComputedScores | writePresetComputedScores |
Ingest limits
Defined in lib/github/ingest-limits.ts:
| Constant | Value |
|---|---|
MAX_COMMITS_PER_REPO | 100 |
MAX_PULLS_PER_REPO | 50 |
MAX_ISSUES_PER_REPO | 50 |
REPO_CONCURRENCY | 6 |
DEFAULT_LOOKBACK_DAYS | 90 |
PAGE_SAFETY_CAP | 20 |
REPO_CONCURRENCY = 6 means at most 6 repositories are fetched in parallel during a single ingest run. PAGE_SAFETY_CAP = 20 limits the GraphQL pagination depth per query regardless of total result size.
Time constants
Defined in lib/constants/time.ts:
SCORE_TIME_PERIODS = ['today', 'week', 'month', 'quarter', 'half_year', 'all_time'];
INGEST.COOLDOWN_MS = 86_400_000; // 24 hours
INGEST.TTL_MS = {
today: 60_000, // 1 minute
week: 300_000, // 5 minutes
month: 900_000, // 15 minutes
quarter: 900_000, // 15 minutes
half_year: 1_800_000, // 30 minutes
all_time: 1_800_000, // 30 minutes
};Signal types (DB enum signal_type)
commit | pr_open | pr_merge | pr_close_no_merge |
issue_open | issue_close | review | review_comment | comment | spamDefined in migration 001_enums_and_utilities.sql.
Score-related DB enums
These three enums are defined in migration 007_scoring_presets.sql (not 001):
| Enum | Values |
|---|---|
review_state | approved, changes_requested, commented |
multiplier_kind | merged_pr_commit, pr_linked_to_issue, first_activity |
zero_point_kind | self_review, self_merge, bot_activity, issue_closed_no_pr, pr_closed_no_merge |
Leaderboard scope/entity/period enums (in 001):
| Enum | Values |
|---|---|
score_scope_type | organization, team, repository |
score_entity_type | contributor, team, repository |
score_time_period | today, week, month, quarter, half_year, all_time |
Redis keys
| Key pattern | Purpose |
|---|---|
leaderboard:lock:{stableIngestScopeId} | Mutex preventing concurrent ingests for the same scope |
| Advisory lock per preset + scope | Prevents concurrent writePresetComputedScores for the same materialization |
| Response cache key (per route) | Short-lived cache of serialised API responses |
Edge cases
all_time period
Fully implemented. getDateRangeForScoreTimePeriod('all_time') returns { from: null, to: null }. getSignalsForOrg() called with no date filter returns all rows in the partitioned signals table. TTL is 30 minutes. Any TODO comment referencing all_time as unimplemented is stale.
Custom date range
Requests with from/to params bypass computed_scores entirely. computeCustomDateRange() in lib/leaderboard/serve.ts fetches signals from the DB and aggregates in-memory. Results are not persisted. The Redis response cache still applies.
Concurrent ingests
The Redis lock on leaderboard:lock:{stableIngestScopeId} guarantees only one ingest runs at a time for a given scope. A second concurrent request finds the lock held and skips the ingest phase, proceeding directly to serve.
setLastSuccessfulIngestAt race condition
lib/supabase/ingest-state.ts handles the upsert-race condition with a retry. If two processes attempt to create the initial ingest_log row simultaneously, one retries the upsert after the conflict.
Bot signals
Bot users are stored in the users table with is_bot = true. Their signals are stored normally in the signals table. The scoring engine zeroes their score when the bot_activity zero-point condition is enabled. Disabling the condition in a preset allows bot commits to contribute to repository and team scores.
Signals FK restriction
The signals table uses RESTRICT (not CASCADE) for its foreign keys to users and repositories. Deleting a user or repository that has signal data will fail at the DB level. Use deleteSignalsForInstallation() first to purge signals before removing the installation.
Diminishing returns floor
At excess = 8 (17 commits/week with default settings), factor = max(0.2, 1 − 0.88) = 0.2. Further commits continue to score at 20% of base — not zero. The floor fraction is configurable per preset via scoring_preset_rules.floor_fraction.
Co-author signal splitting
normalizeGitHubData() splits commit additions and deletions proportionally across co-authors listed in the commit. Each co-author receives a signal row for the same commit event with their proportional additions/deletions. The content hash differs because the user component of the hash changes.