docs: server comments say what the code does, not how it came to be (#6277)
Comment blocks in cmd/minstrel and 30 smaller internal packages, rewritten
to rule 32's standard with each citation read in context: task, milestone
and rule ids go, and so do the stories they were attached to ("used to",
"Before #N", measurements from one install, quotes, pointers to Fabled
Librarian's records and to docs/superpowers files that are not in the
repo). Where a story wrapped a real constraint, the constraint stays as a
present-tense fact.
Comments that had gone stale are corrected:
- taste.Config: the tuned values are overlaid by internal/recsettings;
it is no longer "a later follow-up".
- StorageConfig: the data dir holds art, caches and the stream secret,
not only playlist covers.
- the Subsonic album list and getUser comments describe what they do
today rather than a milestone that would change it.
Checked: the non-comment token stream of every changed file matches
HEAD, and gofmt -l is empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,15 @@
|
||||
// Package recsettings is the DB-backed home of the recommendation
|
||||
// tuning knobs (#1250): the two scoring-weight profiles (radio /
|
||||
// daily_mix) and the taste-profile build settings (engagement
|
||||
// half-life + completion curve). It follows the coverart
|
||||
// tuning knobs: the scoring-weight profiles (radio, daily_mix,
|
||||
// songs_like), Discover's settings, and the taste-profile build
|
||||
// settings (engagement half-life + completion curve). It follows the coverart
|
||||
// SettingsService pattern — boot reconcile seeds shipped defaults for
|
||||
// missing rows, values are cached under a RWMutex, and every change
|
||||
// takes effect live (the daily_mix profile + taste config are pushed
|
||||
// into package playlists; radio reads the cache per request).
|
||||
//
|
||||
// Framing (decision #1247): this is the defaults-discovery lab. The
|
||||
// operator turns knobs to FIND good values; found-good values get
|
||||
// baked back into the Shipped* functions below as new shipped
|
||||
// defaults. End users and other operators should never need the card.
|
||||
// This is a lab for finding good defaults: an admin turns knobs to find
|
||||
// good values, and those get baked back into the Shipped* functions below
|
||||
// as new shipped defaults. A typical install never needs to touch it.
|
||||
package recsettings
|
||||
|
||||
import (
|
||||
@@ -35,24 +34,23 @@ const (
|
||||
ScopeRadio = "radio"
|
||||
ScopeDailyMix = "daily_mix"
|
||||
ScopeTaste = "taste"
|
||||
// ScopeDiscover is the Discover request surface (#2377). Its own scope
|
||||
// rather than columns on taste: SnoozeDays lives here, and a snooze must
|
||||
// never be read as taste signal (#2374) — filing it under taste would put
|
||||
// it one careless join from the leak that design forbids.
|
||||
// ScopeDiscover is the Discover request surface. Its own scope rather
|
||||
// than columns on taste: SnoozeDays lives here, and a snooze must never
|
||||
// be read as taste signal — filing it under taste would put it one
|
||||
// careless join from that leak.
|
||||
ScopeDiscover = "discover"
|
||||
// ScopeSongsLike is the "Songs like {X}" surface (#3881). It shared
|
||||
// daily_mix with For-You until 2026-09-10, and that sharing WAS the bug:
|
||||
// the two surfaces want opposite things. For-You answers "what will they
|
||||
// enjoy today" and is supposed to roam; Songs-like answers "what sounds
|
||||
// like THIS" and is the tightest surface in the product. One set of
|
||||
// weights cannot serve both, and the broad answer was winning.
|
||||
// ScopeSongsLike is the "Songs like {X}" surface. It is separate from
|
||||
// daily_mix because the two want opposite things: For-You answers "what
|
||||
// will they enjoy today" and is supposed to roam; Songs-like answers
|
||||
// "what sounds like THIS" and is the tightest surface in the product. One
|
||||
// set of weights cannot serve both.
|
||||
ScopeSongsLike = "songs_like"
|
||||
)
|
||||
|
||||
// TasteTuning is the tunable subset of taste.Config: the engagement
|
||||
// half-life, the completion→engagement curve points, the enriched-tag
|
||||
// weight (how much folksonomy tags count vs raw ID3 genre, #1520), and the
|
||||
// era-facet weight (how strongly a decade-play imprints, #1530).
|
||||
// weight (how much folksonomy tags count vs raw ID3 genre), and the
|
||||
// era-facet weight (how strongly a decade-play imprints).
|
||||
type TasteTuning struct {
|
||||
HalfLifeDays float64
|
||||
EngagementHardSkip float64
|
||||
@@ -63,12 +61,10 @@ type TasteTuning struct {
|
||||
MoodScale float64
|
||||
}
|
||||
|
||||
// ShippedRadioWeights are the shipped radio-profile defaults (moved
|
||||
// here from config.RecommendationConfig — YAML is bootstrap-only,
|
||||
// rule: config in UI). Radio is seed-directed (the user picked a
|
||||
// direction), so taste is a lighter nudge than in the daily mixes.
|
||||
// ContextTimeWeight starts uniform (1.0) across both profiles pending
|
||||
// trend data (#1531); split them once the metrics view justifies it.
|
||||
// ShippedRadioWeights are the shipped radio-profile defaults. Radio is
|
||||
// seed-directed (the user picked a direction), so taste is a lighter nudge
|
||||
// than in the daily mixes. ContextTimeWeight is 1.0 in both profiles; the
|
||||
// metrics view is what would justify splitting them.
|
||||
func ShippedRadioWeights() recommendation.ScoringWeights {
|
||||
return recommendation.ScoringWeights{
|
||||
BaseWeight: 1.0,
|
||||
@@ -99,12 +95,11 @@ func ShippedDailyMixWeights() recommendation.ScoringWeights {
|
||||
}
|
||||
}
|
||||
|
||||
// ShippedSongsLikeWeights are the shipped songs_like-profile defaults
|
||||
// (#3881). The whole point is that SIMILARITY DOMINATES; every other
|
||||
// profile balances it against taste and engagement, and this one must not.
|
||||
// ShippedSongsLikeWeights are the shipped songs_like-profile defaults. The
|
||||
// whole point is that SIMILARITY DOMINATES; every other profile balances it
|
||||
// against taste and engagement, and this one must not.
|
||||
//
|
||||
// The failure being corrected, arithmetic from the daily_mix profile that
|
||||
// this surface used to share:
|
||||
// Under the daily_mix weights, for example:
|
||||
//
|
||||
// unrelated track, liked, not played recently → 1.0 + 2.0 + 1.0 = 4.0
|
||||
// PERFECT similarity match, not liked → 1.0 + 1.5 = 2.5
|
||||
@@ -132,12 +127,10 @@ func ShippedDailyMixWeights() recommendation.ScoringWeights {
|
||||
// stays 2.0 because a track the user skips is still unwanted no matter how
|
||||
// similar it is.
|
||||
//
|
||||
// These are DEFAULTS, not settings (rule 25) — the operator turns them in the
|
||||
// admin tuning card and good values get baked back here. They are a
|
||||
// defensible starting point rather than a measured optimum: the per-arm fill
|
||||
// rates and the real sim_score distribution are still unknown (#3879), and
|
||||
// `likes_overlap` contributes a flat 0.3 that a high SimilarityWeight
|
||||
// amplifies. Expect to move these once that lands.
|
||||
// These are DEFAULTS, not settings: an admin turns them in the tuning card
|
||||
// and good values get baked back here. They are a defensible starting point
|
||||
// rather than a measured optimum, and `likes_overlap` contributes a flat 0.3
|
||||
// that a high SimilarityWeight amplifies.
|
||||
func ShippedSongsLikeWeights() recommendation.ScoringWeights {
|
||||
return recommendation.ScoringWeights{
|
||||
BaseWeight: 1.0, // same for all candidates; differentiates nothing
|
||||
@@ -169,12 +162,12 @@ func shippedWeightsFor(scope string) (recommendation.ScoringWeights, bool) {
|
||||
return recommendation.ScoringWeights{}, false
|
||||
}
|
||||
|
||||
// DiscoverTuning is the tunable set for the Discover request surface (#2377).
|
||||
// DiscoverTuning is the tunable set for the Discover request surface.
|
||||
type DiscoverTuning struct {
|
||||
// TagOverlapWeight scales the taste-tag term: score × (1 + w × overlap).
|
||||
// 0 disables it and restores pure similarity ranking.
|
||||
TagOverlapWeight float64
|
||||
// SnoozeDays is the default "not right now" duration (#2374).
|
||||
// SnoozeDays is the default "not right now" duration.
|
||||
SnoozeDays float64
|
||||
}
|
||||
|
||||
@@ -183,12 +176,11 @@ type DiscoverTuning struct {
|
||||
// TagOverlapWeight 1.0 lets a perfect tag match at most double a candidate's
|
||||
// similarity score — enough to reorder the deck meaningfully, not enough for a
|
||||
// popular-tag coincidence to beat a genuinely strong similarity match. It is a
|
||||
// starting point for the tuning lab, not a tuned value: the honest way to pick
|
||||
// it is the metrics trend view after some real use.
|
||||
// starting point, not a tuned value: the way to pick it is the metrics trend
|
||||
// view after some real use.
|
||||
//
|
||||
// SnoozeDays 90 matches the operator's approved shape: long enough that a
|
||||
// parked suggestion stops nagging, short enough that a taste shift brings it
|
||||
// back on its own.
|
||||
// SnoozeDays 90: long enough that a parked suggestion stops nagging, short
|
||||
// enough that a taste shift brings it back on its own.
|
||||
func ShippedDiscoverTuning() DiscoverTuning {
|
||||
return DiscoverTuning{
|
||||
TagOverlapWeight: 1.0,
|
||||
@@ -337,7 +329,7 @@ func (s *Service) Taste() TasteTuning {
|
||||
|
||||
// Discover returns the cached Discover-tuning values. Read per request by the
|
||||
// suggestions handler, so an admin change takes effect on the next refresh
|
||||
// with no restart (rule #25).
|
||||
// with no restart.
|
||||
func (s *Service) Discover() DiscoverTuning {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
Reference in New Issue
Block a user