5.7 KiB
5.7 KiB
Session — 07 July 2026
Scaffolded: v2 Recommendation Engine — code complete, not yet deployed (Systems A–E + Phase 4)
All six axioms encoded as running code. Every claim is evidence, not fact. MusicBrainz is the structural spine, not truth. Conflicts coexist in the graph.
Files created (8 new)
| File | Lines | System |
|---|---|---|
backend/src/services/generators.service.ts |
446 | C — 8 candidate generators |
backend/src/services/session-director.service.ts |
761 | D — Session planner + fatigue + arcs |
backend/src/services/discovery.service.ts |
297 | E — Graph walks + probation lifecycle |
backend/src/services/image-enrichment.service.ts |
105 | Phase 4 — Image candidate pipeline |
workers/src/mb-spine-writer.ts |
136 | A — MB artist-credit → claims writer (wired into enrichment.service.ts) |
backend/src/routes/graph.routes.ts |
152 | Graph API (claims, fusion, sources, evidence) |
backend/src/routes/v2.routes.ts |
130 | v2 vibe endpoints (start, next, feedback, state) |
backend/src/routes/discovery.routes.ts |
103 | Discovery + image API endpoints |
Files modified (3)
| File | Changes |
|---|---|
backend/src/db/schema.sql |
+187 lines: 9 new tables + 2 ALTER TABLE + indexes |
backend/src/services/db.service.ts |
+700 lines: 6 migrations, 12 new methods, 6 interfaces, evidence wiring in recordPlay/recordSkip/recordFeedback/dislikeTrack, listener-behavior writer in recordPlay |
backend/src/app.ts |
+4 lines: imports + registrations for v2 + discovery routes |
System-by-system
System A — Knowledge Graph (probabilistic fusion)
source_trusttable: configurable trust weights (mb=0.90, tag=0.30, listener_behavior=0.40)claimstable: graph spine, unique on (subject, pred, object, source, user_id)claim_fusionview: weighted vote SUM(trust × confidence × recency)track_artists_v2/album_artists_v2: compatibility views over fusionrecording_mbidon tracks: structural spine anchor- Methods: upsertClaim, upsertClaims, getClaimsBySubject, getFusedValue, getFusedTrackArtists
- Backfill migration
20260707_backfill_claims: existing track_artists → claims (tag), artist_similar → same_scene_as (lastfm)
System B — Listener Model
evidencetable: append-only signal streamlistener_beliefstable: per-profile beliefs with decay- Every play/skip/feedback writes evidence rows automatically
- Methods: recordEvidence, recordEvidenceBatch, getListenerBeliefs, updateListenerBelief
System C — Candidate Generators (8 generators)
comfortGenerator: longterm affinity > 0.5 artistsadjacentGenerator: 2-hop graph walks from seed artistdiscoveryGenerator: unfamiliar artists via same_scene_as from trusted artists, gated by novelty_tolerancedeepDiveGenerator: obsession album deep cuts in album orderrevivalGenerator: stale high-affinity artists (>90d untouched)experimentalGenerator: random unfamiliar genrescontextualGenerator: context-tagged preferencesnoveltyGenerator: recent releases (≤60d) via same_scene_as/same_label_as/produced edges from trusted artists- All candidates carry non-empty
ClaimEdge[]explanations (graph paths)
System D — Session Director (runs alongside v1; getNextVibeChunk not yet deleted)
buildState: energy from last 5 plays, novelty_hunger from discovery profile, session agecomputeFatigue: exponential decay per dimension (track/7d-30d, artist/24h-8h, genre/24h-8h, language/2h-1h)getBudgets: reads diversity_budgets, calculates spend from recent historypickArc/getArcSlots: energy+novelty-based arc templates (comfort/discovery/energetic/late-night)rankCandidates: multi-objective weighted sum (enjoyment, fatigue, diversity, entropy, repetition)detectAntiLoop: Herfindahl-Hirschman Index + fatigue thresholdbuildPlan/replan: full orchestration loop with slot filling- 27KB of planner logic
System E — Acquisition Pipeline
walkGraphForDiscovery: walks same_scene_as/featured_on edges to artists not in libraryevalCandidates: fused relevance + novelty tolerance + diversity check → acquire/retireevalProbation/sweepProbation: evidence-based retain/retire lifecyclerunMetaLearning: discovery source retention analysisdiscovery_candidates+probation_statuscolumns
Phase 4 — Image Candidates
fetchImagesForArtist/fetchImagesForAlbum: write candidate rows per sourceselectBestImage: source-priority-tiered selection, updates image_path/artwork_idimage_candidatestable with source/verified tracking
Evidence wiring (every interaction)
- recordPlay → playback_completed (longterm +0.10) + replay_within_24h if applicable + alias_of/same_scene_as behavior claims
- recordSkip → skip_quick (negative -0.20)
- recordFeedback(promoted) → add_to_favorites (longterm +0.60)
- recordFeedback(disliked) → hidden (negative -0.60)
- dislikeTrack → hidden (negative -0.60)
Verification
npx tsc --noEmit— 0 errorsnpx vitest run— 30/30 pass (mocked shape checks, not DB-state)- No git repo — changes uncommitted
- NOT deployed: live backend container is pre-v2;
/api/v2/*and/api/graph/*return 404; DB has zero v2 tables. Seedocs/architecture/v2-fix-plan.mdfor the fix + deploy plan.
Next
- Execute
docs/architecture/v2-fix-plan.md(MV refresh, decay job, bug fixes, deploy) - After deploy + verify: wire the v2 endpoint into the frontend Vibe page (replace v1 vibeService calls)
- Build yt-dlp worker for System E acquisition (download candidates)
- After v2 is verified in production: delete v1 CTE (
getNextVibeChunk),vibe.routes.ts,feedbacktable,artist_similartable per the doc's "Retiring v1" list