Documentation
¶
Index ¶
- Constants
- type ActiveTenant
- type BehavioralSignalsdeprecated
- type DailyActivityCount
- type DailyCount
- type DailyScoringCount
- type DigestTarget
- type HourlyCount
- type HourlySummary
- type NotificationEvent
- type NotificationEventFilter
- type PREventRow
- type PREventsStats
- type PipelineStats
- type PoolConfig
- type QueueEntry
- type ScoreHistoryEntry
- type StaleContributor
- type Store
- func (s *Store) BatchUpsertActivity(ctx context.Context, summaries []HourlySummary) (int, error)
- func (s *Store) BatchUpsertPREvents(ctx context.Context, events []PREventRow) (int, error)
- func (s *Store) BumpScoredAt(ctx context.Context, username, provider string) error
- func (s *Store) Close() error
- func (s *Store) CompactActivity(ctx context.Context, olderThan time.Duration) (int64, error)
- func (s *Store) ContributorCount(ctx context.Context) (int, error)
- func (s *Store) ContributorExists(ctx context.Context, username, provider string) (bool, error)
- func (s *Store) CreateWatchlist(ctx context.Context, tenantID, target, source string) error
- func (s *Store) DB() *sql.DB
- func (s *Store) DailyActivityCounts(ctx context.Context, days int) ([]DailyCount, error)
- func (s *Store) DailyScoringCounts(ctx context.Context, days int) ([]DailyCount, error)
- func (s *Store) DeleteWatchlist(ctx context.Context, watchlistID, tenantID string) error
- func (s *Store) DequeueForScoring(ctx context.Context, limit int) ([]QueueEntry, error)
- func (s *Store) DisableWatchlistEmails(ctx context.Context, tenantID string) error
- func (s *Store) EnqueueForScoring(ctx context.Context, username, provider string, priority int) error
- func (s *Store) EnsureImplicitWatchlist(ctx context.Context, tenantID, targetLogin string) error
- func (s *Store) GetAllWatchlistTargets(ctx context.Context) (map[string][]WatchlistEntry, error)
- func (s *Store) GetAuthoredMergedPRCount(ctx context.Context, provider, username string) (int, error)
- func (s *Store) GetBehavioralSignals(ctx context.Context, username, provider string) (*BehavioralSignals, error)
- func (s *Store) GetCachedSignals(ctx context.Context, username, provider string) (*score.InputSignals, error)
- func (s *Store) GetCrossVCS(ctx context.Context, provider, username string) (*model.CrossVCS, time.Time, error)
- func (s *Store) GetCurrentGrade(ctx context.Context, username, provider string) (string, error)
- func (s *Store) GetLifetimeActivity(ctx context.Context, username, provider string) (*model.LifetimeActivity, error)
- func (s *Store) GetNotificationEvents(ctx context.Context, tenantID string, f NotificationEventFilter, ...) ([]NotificationEvent, int, error)
- func (s *Store) GetOSSFScorecard(ctx context.Context, provider, owner, repo string) (*model.OSSFScorecard, time.Time, error)
- func (s *Store) GetPublisherProfile(ctx context.Context, provider, username, registry string) (*model.RegistryProfile, time.Time, error)
- func (s *Store) GetRepoSummary(ctx context.Context, username, provider string) (*model.OwnedRepos, time.Time, error)
- func (s *Store) GetScoreHistory(ctx context.Context, username, provider string, limit int) ([]ScoreHistoryEntry, error)
- func (s *Store) GetSecurityCredits(ctx context.Context, username, provider string) (*model.SecurityCredits, time.Time, error)
- func (s *Store) GetStackOverflowProfile(ctx context.Context, provider, username string) (*model.StackOverflow, time.Time, error)
- func (s *Store) GetStaleContributors(ctx context.Context, lowDays, highDays, limit int) ([]StaleContributor, error)
- func (s *Store) GetSyncState(ctx context.Context, key string) (time.Time, error)
- func (s *Store) GetTenantRepos(ctx context.Context) (map[string]bool, error)
- func (s *Store) GetTenantsWithUnsentEvents(ctx context.Context) ([]DigestTarget, error)
- func (s *Store) GetTokenQuotaSamples(ctx context.Context, since time.Time) ([]TokenQuotaSample, error)
- func (s *Store) GetTopContributedRepos(ctx context.Context, username, provider string, limit int) ([]model.RepoContribution, error)
- func (s *Store) GetUnsentEventsForDigest(ctx context.Context, tenantID string, limit int) ([]NotificationEvent, error)
- func (s *Store) GetWatchlistsForContributor(ctx context.Context, username, provider string) ([]WatchlistEntry, error)
- func (s *Store) Go(fn func())
- func (s *Store) HourlyActivityCounts(ctx context.Context, hours int) ([]HourlyCount, error)
- func (s *Store) HourlyScoringCounts(ctx context.Context, hours int) ([]HourlyCount, error)
- func (s *Store) InsertNotificationEvent(ctx context.Context, watchlistID, eventType, username string, ...) error
- func (s *Store) LastDigestSentAt(ctx context.Context, tenantID string) (*time.Time, error)
- func (s *Store) ListActiveTenants(ctx context.Context) ([]ActiveTenant, error)
- func (s *Store) ListWatchlists(ctx context.Context, tenantID string) ([]Watchlist, error)
- func (s *Store) MarkAllEventsSent(ctx context.Context, tenantID string) error
- func (s *Store) Migrate(ctx context.Context) error
- func (s *Store) PREventsStats(ctx context.Context) (*PREventsStats, error)
- func (s *Store) PipelineStats(ctx context.Context) (*PipelineStats, error)
- func (s *Store) PruneActivity(ctx context.Context, retention time.Duration) (int64, error)
- func (s *Store) PruneNotificationEvents(ctx context.Context, tenantID string, retentionDays, maxEvents int) (int, error)
- func (s *Store) PrunePREvents(ctx context.Context, retention time.Duration) (int64, error)
- func (s *Store) PruneScoreHistory(ctx context.Context, retention time.Duration) (int64, error)
- func (s *Store) PruneSecurityCredits(ctx context.Context, retention time.Duration) (int64, error)
- func (s *Store) PurgeNonTenantQueue(ctx context.Context) (int64, error)
- func (s *Store) PurgeTokenQuotaSamples(ctx context.Context, before time.Time) (int64, error)
- func (s *Store) QueueDepth(ctx context.Context) (int, error)
- func (s *Store) RecordTokenQuotaSample(ctx context.Context, installationID int64, login string, ...) error
- func (s *Store) RemoveFromQueue(ctx context.Context, username, provider string) error
- func (s *Store) SaveCrossVCS(ctx context.Context, provider, username string, summary *model.CrossVCS) error
- func (s *Store) SaveOSSFScorecard(ctx context.Context, provider, owner, repo string, card *model.OSSFScorecard) error
- func (s *Store) SavePublisherProfile(ctx context.Context, provider, username, registry string, ...) error
- func (s *Store) SaveRepoSummary(ctx context.Context, username, provider string, summary *model.OwnedRepos) error
- func (s *Store) SaveScoreHistory(ctx context.Context, username, provider string, score float64, grade string, ...) error
- func (s *Store) SaveSecurityCredits(ctx context.Context, username, provider string, credits []model.SecurityCredit) error
- func (s *Store) SaveStackOverflowProfile(ctx context.Context, provider, username string, profile *model.StackOverflow) error
- func (s *Store) SaveSyncState(ctx context.Context, key string, val time.Time) error
- func (s *Store) ScoredCount(ctx context.Context) (int, error)
- func (s *Store) StaleCount(ctx context.Context, lowDays, highDays int) (int, error)
- func (s *Store) ToggleWatchlistEmail(ctx context.Context, watchlistID, tenantID string) error
- func (s *Store) UnsentEventCount(ctx context.Context, tenantID string) (int, error)
- func (s *Store) UpdateReputation(ctx context.Context, username, provider string, value float64, ...) error
- func (s *Store) UpsertContributor(ctx context.Context, username, provider string) error
- func (s *Store) WaitBackground(ctx context.Context) error
- func (s *Store) WatchlistManualCount(ctx context.Context, tenantID string) (int, error)
- type TokenQuotaSample
- type Watchlist
- type WatchlistEntry
Constants ¶
const ( EventTypeScoreChange = "score_change" EventTypeNewContributor = "new_contributor" )
Watchlist notification event types. Exported so other packages writing notification events use the same canonical strings.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ActiveTenant ¶
ActiveTenant is a minimal tenant projection used by background jobs.
type BehavioralSignals
deprecated
type DailyActivityCount ¶
type DailyActivityCount = DailyCount
DailyActivityCount holds a single day's total activity count.
type DailyCount ¶
DailyCount holds a single day's count for chart rendering.
type DailyScoringCount ¶
type DailyScoringCount = DailyCount
DailyScoringCount holds one day's scoring count.
type DigestTarget ¶
DigestTarget holds tenant info for sending a digest email.
type HourlyCount ¶
type HourlyCount = DailyCount
HourlyCount is an alias for DailyCount used for hourly chart rendering.
type HourlySummary ¶
type HourlySummary struct {
Username string
Provider string
Hour time.Time
PRsOpened int
PRsMerged int
PRsClosed int
ReviewsGiven int
IssueComments int
IssuesOpened int
IssuesClosed int
DistinctRepos int
Repos []string
}
HourlySummary represents one hour of aggregated contributor activity from GH Archive.
type NotificationEvent ¶
type NotificationEvent struct {
ID int64
EventType string
Username string
Target string // joined from watchlist
Details map[string]any
CreatedAt time.Time
SentAt *time.Time
}
NotificationEvent represents a detected watchlist event for display.
func (NotificationEvent) DetailSummary ¶
func (e NotificationEvent) DetailSummary() string
DetailSummary returns a human-readable verb phrase. Never returns "" for known event types; falls back to "first activity" when no counts are present.
func (NotificationEvent) RepoSummary ¶
func (e NotificationEvent) RepoSummary() string
RepoSummary returns the Repo column display string. Empty when no repos are recorded. For a single repo, returns the bare repo name (org prefix stripped). For multiple repos, returns "first +N" where N is the count of remaining repos.
func (NotificationEvent) RepoTooltip ¶
func (e NotificationEvent) RepoTooltip() string
RepoTooltip returns the full comma-joined list of repos for the HTML title attribute. Returns "" when no repos are recorded.
type NotificationEventFilter ¶
NotificationEventFilter narrows GetNotificationEvents results by column. Empty fields are ignored. Contributor matches ne.username (substring, case-insensitive). Org matches w.target (the watched org/repo target). Repo matches any repo in ne.details->'repos'. Since restricts to events with created_at >= the given time.
type PREventRow ¶
type PREventRow struct {
Provider string
Repo string
Number int
Action string
Author string
OccurAt time.Time
}
PREventRow is the per-archive-hour observation written to the merge-graph table. Action is one of "opened", "merged", "closed". For the opened path Author is populated; for merged/closed it's empty (the actor is typically a CI bot we don't credit).
type PREventsStats ¶
type PREventsStats struct {
TotalRows int
Last24hRows int
AuthorAttributionPct float64
BotOpenedRows int
MissingOpenRows int
IngestAttributionPct float64
LastEventAt time.Time
}
PREventsStats holds operational metrics for the merge-graph table.
IngestAttributionPct is the load-bearing pipeline-health number: of rows where we observed an opened event by a human (i.e. could have attributed authorship), what share did we actually attribute. Denominator = withAuthor + missingOpen — excludes bot-opened rows (intentional NULL author per aggregator policy) so it isolates true ingest gaps from structural NULLs. Near 100% on a healthy pipeline.
AuthorAttributionPct is the overall withAuthor/total share. It conflates bot-opened and missing-open rows so a low value can mean "lots of Renovate churn" or "ingest dropping opens" — useful as a raw signal but not load-bearing. Prefer IngestAttributionPct.
BotOpenedRows is rows where the opened event WAS observed but the opener was a bot (author stripped at aggregator). Renovate / Dependabot dominate this bucket.
MissingOpenRows is rows with a merge or close event but no opened event observed. Driven by the backfill horizon: PRs opened before our earliest archive hour but merged inside it will permanently lack opened_at. Steady-state share, not a transient.
type PipelineStats ¶
PipelineStats holds pipeline health metrics inferred from DB timestamps.
type PoolConfig ¶
type PoolConfig struct {
AppName string
MaxOpenConns int
MaxIdleConns int
ConnMaxLifetime time.Duration
ConnMaxIdleTime time.Duration
}
func DefaultPoolConfig ¶
func DefaultPoolConfig() PoolConfig
type QueueEntry ¶
QueueEntry represents a pending scoring request.
type ScoreHistoryEntry ¶
type ScoreHistoryEntry struct {
Score float64 `json:"score"`
Grade string `json:"grade"`
ScoredAt time.Time `json:"scored_at"`
}
ScoreHistoryEntry represents a single point on a score trend chart.
type StaleContributor ¶
StaleContributor represents a contributor whose score is outdated.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
func (*Store) BatchUpsertActivity ¶
BatchUpsertActivity upserts hourly summaries into devtrace_contributor_activity. On conflict, counts are added to existing values. Returns the number of rows upserted. All rows are written in a single transaction, so partial progress is impossible; on error the entire batch is rolled back and count 0 is returned.
func (*Store) BatchUpsertPREvents ¶
BatchUpsertPREvents writes per-PR observations into the merge graph. Each row stamps only the column corresponding to its action; the COALESCE-based UPDATE preserves earlier values so an opened event in March + a merged event in May land in the same row with both timestamps populated.
Bot-opened PRs (Renovate, Dependabot, etc.) arrive with empty Author by aggregator-side filtering; their opened_at still gets stamped so we can recognize the PR existed, but they won't show up in author-attributed merge counts.
func (*Store) BumpScoredAt ¶
BumpScoredAt advances a contributor's scored_at timestamp without changing their score. Used to move permanently-failing contributors (404, 451) out of the stale pool so they aren't retried every 30 seconds.
func (*Store) CompactActivity ¶
CompactActivity aggregates hourly rows older than the given age into weekly buckets (Monday 00:00 UTC), then deletes the originals. Runs in a single transaction so a failure leaves data unchanged. Returns rows deleted.
func (*Store) ContributorCount ¶
ContributorCount returns the exact number of known contributors.
func (*Store) ContributorExists ¶
ContributorExists checks whether a reputation row exists for a contributor.
func (*Store) CreateWatchlist ¶
CreateWatchlist adds a manual watchlist entry. Uses ON CONFLICT DO NOTHING for idempotency.
func (*Store) DailyActivityCounts ¶
DailyActivityCounts returns per-day total activity counts for the last N days.
func (*Store) DailyScoringCounts ¶
DailyScoringCounts returns per-day scoring counts for the last N days.
func (*Store) DeleteWatchlist ¶
DeleteWatchlist removes a manual watchlist entry. The tenant_id check ensures a tenant can only delete their own watchlists.
func (*Store) DequeueForScoring ¶
DequeueForScoring atomically removes and returns the highest-priority entries from the scoring queue. Priority 1 is highest. Uses DELETE ... RETURNING to ensure entries are exclusively claimed even under concurrent consumers.
func (*Store) DisableWatchlistEmails ¶
DisableWatchlistEmails sets notify_email = false for all watchlists belonging to a tenant. Used by the one-click unsubscribe handler.
func (*Store) EnqueueForScoring ¶
func (s *Store) EnqueueForScoring(ctx context.Context, username, provider string, priority int) error
EnqueueForScoring adds a contributor to the scoring queue. On conflict, keeps the highest priority (lowest number) and updates queued_at only when priority is upgraded.
func (*Store) EnsureImplicitWatchlist ¶
EnsureImplicitWatchlist creates an implicit watchlist entry for a GitHub App installation. Idempotent: does nothing if the entry already exists.
func (*Store) GetAllWatchlistTargets ¶
GetAllWatchlistTargets returns all active watchlist targets grouped by target. Each target maps to a slice of watchlist entries so the ingest pipeline can match contributor repos against watchlists.
func (*Store) GetAuthoredMergedPRCount ¶
func (s *Store) GetAuthoredMergedPRCount(ctx context.Context, provider, username string) (int, error)
GetAuthoredMergedPRCount returns the number of PRs authored by the given user that have been merged. The query relies on the partial index idx_devtrace_pr_events_author_merged so it stays cheap even as the table grows.
Returns 0 (not nil) for users with no observations, so callers don't need to special-case "never seen". An error is returned only on a real query failure.
func (*Store) GetBehavioralSignals ¶
func (s *Store) GetBehavioralSignals(ctx context.Context, username, provider string) (*BehavioralSignals, error)
GetBehavioralSignals computes behavioral metrics from the last 90 days of devtrace_contributor_activity. Returns nil when no data exists for the contributor.
func (*Store) GetCachedSignals ¶
func (s *Store) GetCachedSignals(ctx context.Context, username, provider string) (*score.InputSignals, error)
GetCachedSignals returns the previously stored signals for a contributor. Returns (nil, nil) when no reputation record exists.
func (*Store) GetCrossVCS ¶
func (s *Store) GetCrossVCS(ctx context.Context, provider, username string) (*model.CrossVCS, time.Time, error)
GetCrossVCS returns the cached cross-VCS match summary for a user. Returns (nil, zero-time, nil) when no row exists.
A row with TotalMatched=0 and an empty Matches slice is the sentinel for "we looked, no shared keys found"; the row keeps a non-zero fetched_at so the service-layer TTL check suppresses re-asking against forges the contributor doesn't use. The UI render path treats len(Matches)==0 as "no cross-VCS data" and omits the section.
func (*Store) GetCurrentGrade ¶
GetCurrentGrade returns the current letter grade for a contributor. Returns "" if no reputation record exists.
func (*Store) GetLifetimeActivity ¶
func (s *Store) GetLifetimeActivity(ctx context.Context, username, provider string) (*model.LifetimeActivity, error)
GetLifetimeActivity returns aggregate counts across the entire activity history for the contributor. Returns nil when no data exists.
func (*Store) GetNotificationEvents ¶
func (s *Store) GetNotificationEvents(ctx context.Context, tenantID string, f NotificationEventFilter, limit, offset int) ([]NotificationEvent, int, error)
GetNotificationEvents returns paginated notification events for a tenant, optionally filtered by per-column predicates. Events are ordered by created_at DESC. Returns the events and the total count of the filtered set.
func (*Store) GetOSSFScorecard ¶
func (s *Store) GetOSSFScorecard(ctx context.Context, provider, owner, repo string) (*model.OSSFScorecard, time.Time, error)
GetOSSFScorecard returns the cached OSSF Scorecard for the given repo. Returns (nil, zero-time, nil) when no row exists. The returned fetched_at lets callers compare against config.OSSFTTL.
A non-nil zero-score Scorecard with empty Checks represents the "fetched but upstream had no scorecard" sentinel, recorded so we don't hammer the OSSF API for repos it has no record of. Callers should treat the zero-score sentinel as "no data" — the UI render path is gated on len(Checks) > 0.
func (*Store) GetPublisherProfile ¶
func (s *Store) GetPublisherProfile(ctx context.Context, provider, username, registry string) (*model.RegistryProfile, time.Time, error)
GetPublisherProfile returns the cached publisher profile for the given user on the given registry. Returns (nil, zero-time, nil) when no row exists. The fetched_at timestamp lets callers decide whether to refresh against config.PublisherTTL.
A zero-PackageCount profile is still returned (not collapsed to nil) so callers can distinguish "we looked, found nothing" from "never looked"; the UI render path treats zero-count as "no publisher data" but the service layer's TTL check uses the non-nil row to suppress repeated re-fetching.
func (*Store) GetRepoSummary ¶
func (s *Store) GetRepoSummary(ctx context.Context, username, provider string) (*model.OwnedRepos, time.Time, error)
GetRepoSummary returns the cached summary for the contributor along with its fetched_at timestamp. Returns (nil, zero-time, nil) when no row exists. Callers compare fetched_at against config.RepoSummaryTTL to decide whether to refresh.
func (*Store) GetScoreHistory ¶
func (s *Store) GetScoreHistory(ctx context.Context, username, provider string, limit int) ([]ScoreHistoryEntry, error)
GetScoreHistory returns up to limit history entries for a contributor, ordered oldest-first (suitable for left-to-right chart rendering).
func (*Store) GetSecurityCredits ¶
func (s *Store) GetSecurityCredits(ctx context.Context, username, provider string) (*model.SecurityCredits, time.Time, error)
GetSecurityCredits returns the aggregate plus the most-recent N advisory credits for the contributor. Returns (nil, zero-time, nil) when no rows exist. The fetched_at returned is the latest fetch timestamp across all rows for this contributor — callers compare it to config.SecurityCreditTTL to decide whether to refresh.
func (*Store) GetStackOverflowProfile ¶
func (s *Store) GetStackOverflowProfile(ctx context.Context, provider, username string) (*model.StackOverflow, time.Time, error)
GetStackOverflowProfile returns the cached SO profile for a user. Returns (nil, zero-time, nil) when no row exists.
A zero-rep / zero-user-id row is the sentinel for "we looked, no SO link declared" (or "the link pointed to a missing user"); the row keeps a non-zero fetched_at so the service-layer TTL check suppresses re-asking. The UI render path treats user_id==0 as "no SO data" and omits the section.
func (*Store) GetStaleContributors ¶
func (s *Store) GetStaleContributors(ctx context.Context, lowDays, highDays, limit int) ([]StaleContributor, error)
GetStaleContributors returns contributors whose scores are older than configured thresholds. Low scores (<0.5): stale after lowDays. High scores (>=0.5): stale after highDays.
func (*Store) GetSyncState ¶
GetSyncState returns the time stored under the given key in sync_state. Returns the zero time if the key does not exist.
func (*Store) GetTenantRepos ¶
GetTenantRepos returns the set of org/user logins with active GitHub App installations. The ingest job uses this to determine if a repo owner is a tenant.
func (*Store) GetTenantsWithUnsentEvents ¶
func (s *Store) GetTenantsWithUnsentEvents(ctx context.Context) ([]DigestTarget, error)
GetTenantsWithUnsentEvents returns tenants that have unsent notification events and have email-enabled watchlists.
func (*Store) GetTokenQuotaSamples ¶
func (s *Store) GetTokenQuotaSamples(ctx context.Context, since time.Time) ([]TokenQuotaSample, error)
GetTokenQuotaSamples returns all quota samples since the given time.
func (*Store) GetTopContributedRepos ¶
func (s *Store) GetTopContributedRepos(ctx context.Context, username, provider string, limit int) ([]model.RepoContribution, error)
GetTopContributedRepos returns the contributor's top repos ranked by distinct active-hour count, limited to `limit` entries. Empty slice when the contributor has no activity data. Uses the activity-table PK prefix (username, provider) — no additional index required.
func (*Store) GetUnsentEventsForDigest ¶
func (s *Store) GetUnsentEventsForDigest(ctx context.Context, tenantID string, limit int) ([]NotificationEvent, error)
GetUnsentEventsForDigest returns unsent notification events for a tenant, capped at limit.
func (*Store) GetWatchlistsForContributor ¶
func (s *Store) GetWatchlistsForContributor(ctx context.Context, username, provider string) ([]WatchlistEntry, error)
GetWatchlistsForContributor returns watchlist entries matching a contributor's recent activity repos. Joins activity repos against watchlist targets.
func (*Store) Go ¶
func (s *Store) Go(fn func())
Go spawns fn in a tracked goroutine. Use this for fire-and-forget DB writes that must outlive the request context (e.g., persisting a score after the API response has been sent). Pair with WaitBackground in the shutdown path so writes drain before Close runs.
func (*Store) HourlyActivityCounts ¶
HourlyActivityCounts returns per-hour total activity counts for the last N hours.
func (*Store) HourlyScoringCounts ¶
HourlyScoringCounts returns per-hour scoring counts for the last N hours.
func (*Store) InsertNotificationEvent ¶
func (s *Store) InsertNotificationEvent(ctx context.Context, watchlistID, eventType, username string, details map[string]any) error
InsertNotificationEvent records a new watchlist notification event.
func (*Store) LastDigestSentAt ¶
LastDigestSentAt returns the most recent sent_at time for a tenant's digest events. Returns nil if no digest has been sent yet.
func (*Store) ListActiveTenants ¶
func (s *Store) ListActiveTenants(ctx context.Context) ([]ActiveTenant, error)
ListActiveTenants returns all tenants with status = 'active'.
func (*Store) ListWatchlists ¶
ListWatchlists returns all watchlists for a tenant.
func (*Store) MarkAllEventsSent ¶
MarkAllEventsSent marks all unsent notification events for a tenant as sent. Called after a digest email is sent — the email contains the top N events but all pending events are cleared so they don't queue up for the next digest.
func (*Store) PREventsStats ¶
func (s *Store) PREventsStats(ctx context.Context) (*PREventsStats, error)
PREventsStats returns aggregate state of the merge-graph table for the admin dashboard. All counts come from a single FILTER-based scan to keep the dashboard query cheap.
func (*Store) PipelineStats ¶
func (s *Store) PipelineStats(ctx context.Context) (*PipelineStats, error)
PipelineStats returns pipeline health indicators from existing tables.
func (*Store) PruneActivity ¶
PruneActivity deletes all activity rows older than the given retention window. Returns rows deleted. Run after CompactActivity to remove both hourly and compacted rows beyond the retention limit.
func (*Store) PruneNotificationEvents ¶
func (s *Store) PruneNotificationEvents(ctx context.Context, tenantID string, retentionDays, maxEvents int) (int, error)
PruneNotificationEvents deletes notification events for a tenant in two passes: first by age (events older than retentionDays), then by cap (keep newest maxEvents). retentionDays <= 0 skips the age pass. maxEvents <= 0 skips the cap pass. Returns total rows deleted.
func (*Store) PrunePREvents ¶
PrunePREvents drops PR-event rows whose last_event_at is older than the retention window. Mirrors PruneActivity's cadence so the merge-graph table doesn't outgrow the activity history that scoring is anchored to.
func (*Store) PruneScoreHistory ¶
PruneScoreHistory deletes reputation history rows older than the given retention window. Returns rows deleted.
func (*Store) PruneSecurityCredits ¶
PruneSecurityCredits drops sentinel rows older than the given window. Real credit rows are kept until the next save replaces them. Returns rows deleted. Currently called from the same digest-sweep cadence as other prune routines.
func (*Store) PurgeNonTenantQueue ¶
PurgeNonTenantQueue removes queued entries that were enqueued with priority 2 (non-tenant contributors). Returns the number of rows deleted.
func (*Store) PurgeTokenQuotaSamples ¶
PurgeTokenQuotaSamples deletes samples older than the given time.
func (*Store) QueueDepth ¶
QueueDepth returns the number of entries in the scoring queue.
func (*Store) RecordTokenQuotaSample ¶
func (s *Store) RecordTokenQuotaSample(ctx context.Context, installationID int64, login string, coreLimit, coreUsed, searchLimit, searchUsed, graphqlLimit, graphqlUsed int) error
RecordTokenQuotaSample writes a single quota snapshot to the time-series table. Pass per-family (limit, used) pairs for core (REST), search, and graphql. Callers that only know about core can pass 0 for the others; existing pre-cutover rows behave the same way.
func (*Store) RemoveFromQueue ¶
RemoveFromQueue deletes a contributor from the scoring queue after processing.
func (*Store) SaveCrossVCS ¶
func (s *Store) SaveCrossVCS(ctx context.Context, provider, username string, summary *model.CrossVCS) error
SaveCrossVCS upserts the cross-VCS row. Pass a non-nil summary with TotalMatched=0 and an empty Matches slice to record a "no shared keys" sentinel — same TTL semantics as a real match, suppressing repeated re-fetching for users without a cross-VCS presence.
func (*Store) SaveOSSFScorecard ¶
func (s *Store) SaveOSSFScorecard(ctx context.Context, provider, owner, repo string, card *model.OSSFScorecard) error
SaveOSSFScorecard upserts the scorecard row for a repo. Pass a non-nil card with Score=0 and no checks to record a "fetch attempted, upstream had nothing" sentinel — same TTL semantics as real data, suppressing repeated re-fetching of repos OSSF doesn't have a scorecard for.
func (*Store) SavePublisherProfile ¶
func (s *Store) SavePublisherProfile(ctx context.Context, provider, username, registry string, profile *model.RegistryProfile) error
SavePublisherProfile upserts the publisher row. Pass a non-nil profile with PackageCount=0 and empty Top to record a "fetched but no packages" sentinel — same TTL semantics as real data, suppressing re-fetching for users with no publisher account.
func (*Store) SaveRepoSummary ¶
func (s *Store) SaveRepoSummary(ctx context.Context, username, provider string, summary *model.OwnedRepos) error
SaveRepoSummary upserts the summary row with fetched_at = NOW(). A nil summary clears optional sub-fields but still records the fetch attempt (so a contributor with zero owned repos isn't refetched every request).
func (*Store) SaveScoreHistory ¶
func (s *Store) SaveScoreHistory(ctx context.Context, username, provider string, score float64, grade string, deep bool) error
SaveScoreHistory inserts a new reputation history entry. The contributor row must already exist (FK constraint).
func (*Store) SaveSecurityCredits ¶
func (s *Store) SaveSecurityCredits(ctx context.Context, username, provider string, credits []model.SecurityCredit) error
SaveSecurityCredits replaces the contributor's credit rows in a single transaction: deletes existing rows, inserts the new set, stamps fetched_at = NOW() on each. An empty `credits` slice still records the fetch attempt by inserting a single sentinel row with advisory_id = "", so the next refresh respects the TTL instead of re-hitting GitHub on every score request for users with no credits.
func (*Store) SaveStackOverflowProfile ¶
func (s *Store) SaveStackOverflowProfile(ctx context.Context, provider, username string, profile *model.StackOverflow) error
SaveStackOverflowProfile upserts the SO profile row. Pass a non-nil profile with UserID=0 and Reputation=0 to record a "no SO link declared" sentinel; the row keeps fetched_at fresh so the service layer's TTL check suppresses repeated re-checks.
func (*Store) SaveSyncState ¶
SaveSyncState upserts the given time value under key in sync_state.
func (*Store) ScoredCount ¶
ScoredCount returns the number of contributors with a reputation score.
func (*Store) StaleCount ¶
StaleCount returns the number of contributors whose scores are stale.
func (*Store) ToggleWatchlistEmail ¶
ToggleWatchlistEmail flips the notify_email flag for a watchlist entry.
func (*Store) UnsentEventCount ¶
UnsentEventCount returns the total number of unsent events for a tenant.
func (*Store) UpdateReputation ¶
func (s *Store) UpdateReputation(ctx context.Context, username, provider string, value float64, grade, version string, deep bool, signals *score.InputSignals) error
UpdateReputation upserts a contributor's reputation score and cached signals.
func (*Store) UpsertContributor ¶
UpsertContributor ensures a contributor row exists for the given identity. Uses ON CONFLICT DO NOTHING so it is idempotent.
func (*Store) WaitBackground ¶
WaitBackground blocks until all tracked goroutines exit or ctx is canceled. Returns ctx.Err() on timeout so callers can log the leak.
type TokenQuotaSample ¶
type TokenQuotaSample struct {
SampledAt time.Time `json:"sampled_at"`
InstallationID int64 `json:"installation_id"`
Login string `json:"login"`
QuotaLimit int `json:"quota_limit"`
QuotaUsed int `json:"quota_used"`
SearchLimit int `json:"search_limit"`
SearchUsed int `json:"search_used"`
GraphQLLimit int `json:"graphql_limit"`
GraphQLUsed int `json:"graphql_used"`
}
TokenQuotaSample is a single rate-limit snapshot for one installation. Core REST, Search, and GraphQL families are sampled together so the admin chart can show which family is closest to its ceiling. Pre-cutover rows have search/graphql limits of 0; consumers treat a zero limit as "no data" for that family rather than 100% utilization.