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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user