docs: wanted, dbtest, recommendation and playlists comments say what the code does (#6277)

The same rewrite as d191a282, for four packages whose comments carried the
most history: tuning stories, quotes from the operator, single-install
measurements in library_scale.go, and 'used to' accounts of bugs. The
constraint each story protected stays as a present-tense reason (why tier 3
is reduced not removed, why the randomised arms take an order seed, why the
seed artist is eligible in Songs-like). Comments naming system_cron.go and
config.RecommendationConfig, neither of which holds what they claimed, now
name the scheduler and internal/recsettings.

Checked: non-comment tokens identical to HEAD; gofmt clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 12:19:15 -04:00
co-authored by Claude Opus 5.5
parent d191a28217
commit fcf5b7e5a8
48 changed files with 391 additions and 501 deletions
+16 -17
View File
@@ -4,11 +4,11 @@
// through artist_similarity_unmatched via a single CTE, and returns top-N
// out-of-library candidates with top-3 attribution seeds resolved to names.
//
// Originally M5c, which seeded from raw likes + plays. That signal grew
// without bound and counted skips as engagement, so a few heavily-played
// artists monopolized every slot and the surface entrenched harder the more
// the user listened. Reworked to seed from the taste profile in issue #2367;
// see internal/db/queries/recommendation.sql for the tiering.
// Seeding from the taste profile rather than raw likes + plays matters: raw
// counts grow without bound and count skips as engagement, so a few
// heavily-played artists would monopolize every slot and entrench harder the
// more the user listened. See internal/db/queries/recommendation.sql for the
// tiering.
package recommendation
import (
@@ -33,12 +33,12 @@ type ArtistSuggestion struct {
Attribution []SeedContribution
// MatchedTags are the candidate's own tags that overlap the user's taste
// profile, strongest first (max 3) — the "matches: shoegaze, melancholic"
// explanation (#2377). Empty when the candidate has no cached tags, which
// is common and not an error: coverage is permanently partial (#2376).
// explanation. Empty when the candidate has no cached tags, which is
// common and not an error: coverage is permanently partial.
MatchedTags []string
// TagOverlap is the [0,1] share of the candidate's tag mass the user likes.
// Exposed for the admin tuning lab — seeing the term's actual distribution
// is how the operator picks a weight rather than guessing at one.
// Exposed for admin tuning — seeing the term's actual distribution is how
// a weight gets picked rather than guessed.
TagOverlap float64
}
@@ -71,8 +71,8 @@ func SuggestArtists(
q := dbq.New(pool)
// Over-fetch so there is something to rotate through. A deterministic
// top-N over a score ordering shows the same faces every day until one is
// requested away — the tail of the ranking was unreachable (#2367
// mechanism 1). Selection from this pool happens in selectSuggestions.
// requested away, leaving the tail of the ranking unreachable. Selection
// from this pool happens in selectSuggestions.
rows, err := q.SuggestArtistsForUser(ctx, dbq.SuggestArtistsForUserParams{
UserID: userID,
Column2: halfLifeDays,
@@ -130,7 +130,7 @@ func SuggestArtists(
})
}
// Taste-tag term (#2377). Applied to the whole over-fetched pool BEFORE
// Taste-tag term. Applied to the whole over-fetched pool BEFORE
// selection, so the rotation and diversity rules in selectSuggestions
// operate on taste-blended scores — boosting only the twelve already
// chosen by similarity would leave the actual re-ranking undone.
@@ -227,9 +227,8 @@ func rotationDay(now time.Time) string {
// Three rules, in order:
//
// 1. Diversity cap. Walking in score order, a candidate is dropped once its
// dominant seed already owns maxPerSeed slots. Nothing capped this before,
// so all twelve slots could be neighbours of one artist and the surface
// read as a single narrow cluster (#2367 mechanism 4).
// dominant seed already owns maxPerSeed slots, so twelve slots cannot all
// be neighbours of one artist and read as a single narrow cluster.
// 2. Head. The best few by score always lead, so the strongest matches are
// never rotated out of sight. Mirrors For You's head/tail shape.
// 3. Tail. The rest of the slots are drawn from the remaining pool ordered by
@@ -263,8 +262,8 @@ func selectSuggestions(pool []ArtistSuggestion, limit int, day string) []ArtistS
}
// Diversity is a PREFERENCE, not a quota that may starve the deck. A user
// whose whole pool hangs off one or two seeds would otherwise get a
// three-card surface, which is worse than the monoculture we were avoiding
// (and is the vanish-or-nothing shape rule #131 exists to prevent). Top up
// three-card surface, which is worse than the monoculture it avoids.
// Top up
// in score order from what the cap set aside.
for _, s := range overflow {
if len(out) >= limit {