feat(taste): era/decade taste facet — #1530
Milestone #160 Opt 2 (era half). A third taste facet alongside artists + genre tags: signed weights over decade buckets ("1990s") derived from albums.release_date, rebuilt daily and scored into the taste match. - Migration 0045: taste_profile_eras table (mirrors taste_profile_tags) + taste_tuning.era_scale column (DEFAULT 0.5). - Build side (internal/taste): Config.EraScale ([0,1] damper, mirrors EnrichedTagScale), accumulate folds each play/like's decade at base*EraScale, persist atomic-replaces the era rows. - Scorer (internal/recommendation): TasteProfile gains an era term (own tanh scale + additive 0.15 share so it never weakens the existing artist/tag signal when a track is undated); candidate queries return album release_date; decadeOf mirrors the builder helper. - Tuning lab: era_scale threaded through recsettings + admin API + web card (auto-renders the new row) + Go/web tests. Mood facet deferred to #1534 (partial enrichment coverage + needs candidate-side enriched-tag loading). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -203,6 +203,27 @@ func TestUpdateTaste_EnrichedTagScale(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateTaste_EraScale(t *testing.T) {
|
||||
pool := newPool(t)
|
||||
s := newService(t, pool)
|
||||
// In-range update persists into cache + the assembled taste config.
|
||||
if err := s.UpdateTaste(context.Background(),
|
||||
map[string]float64{"era_scale": 0.8}); err != nil {
|
||||
t.Fatalf("UpdateTaste: %v", err)
|
||||
}
|
||||
if got := s.Taste().EraScale; got != 0.8 {
|
||||
t.Errorf("Taste().EraScale = %v, want 0.8", got)
|
||||
}
|
||||
if got := s.TasteConfig().EraScale; got != 0.8 {
|
||||
t.Errorf("TasteConfig().EraScale = %v, want 0.8", got)
|
||||
}
|
||||
// Out of [0,1] rejects.
|
||||
if err := s.UpdateTaste(context.Background(),
|
||||
map[string]float64{"era_scale": 1.5}); !errors.Is(err, ErrOutOfRange) {
|
||||
t.Errorf("err = %v, want ErrOutOfRange for 1.5", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReset_RestoresShippedAndAudits(t *testing.T) {
|
||||
pool := newPool(t)
|
||||
s := newService(t, pool)
|
||||
|
||||
Reference in New Issue
Block a user