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:
2026-10-23 12:15:05 -04:00
co-authored by Claude Opus 5.5
parent 9f9609591d
commit d191a28217
107 changed files with 485 additions and 597 deletions
+36 -44
View File
@@ -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()