# For You ## Route - URL: `GET /discover/for-you` - Auth required: yes - Controller: `App\Http\Controllers\Web\DiscoverController::forYou()` - Entry service: `App\Services\Recommendations\RecommendationFeedResolver` ## High-level flow `For You` is not a simple search sort. It is a recommendation pipeline with engine selection, caching, layered candidate generation, reranking, and cursor pagination. The controller: 1. reads `limit` and `cursor` 2. calls `RecommendationFeedResolver::getFeed()` 3. converts feed items into the artwork card view model 4. returns HTML or JSON depending on request type ## Engine selection `RecommendationFeedResolver` chooses between two implementations: - V2: `App\Services\Recommendations\RecommendationServiceV2` - V1: `App\Services\Recommendations\PersonalizedFeedService` Selection is based on: - `config('discovery.v2.enabled')` - rollout percentage bucket for the current user - explicit `algo_version` override ## Cache model Both engines use `user_recommendation_cache`. At request time: 1. Load cache row for `(user_id, algo_version)` 2. Check cache version and `expires_at` 3. If missing or stale, dispatch `RegenerateUserRecommendationCacheJob` 4. If the row is empty, build fallback recommendations inline for the current request This means the page is usually cache-backed, but it does not hard-fail if the cache is cold. ## V2 pipeline V2 is the richer layered engine. ### Candidate layers The candidate pool is blended from: - personalized layer - social layer - trending layer - exploration layer - vector layer (only when V3/vector support is enabled and configured) Default target ratios from `config/discovery.php`: - personalized: 50% - social: 20% - trending: 20% - exploration: 10% ### Main V2 score For each candidate row: ```text score = (base_score * weight_base) + session_boost + social_boost + trending_boost + exploration_boost + creator_boost + vector_boost - negative_penalty - repetition_penalty ``` Where: - `session_boost` comes from merged session/profile signals - `social_boost` comes from followed creators and artworks liked by followed creators - `trending_boost` is built from `trending_score_1h`, `trending_score_24h`, `trending_score_7d` - `exploration_boost` rewards fresh uploads, new creators, and unseen tags - `creator_boost` uses creator follower count plus artwork momentum metrics - `vector_boost` comes from visual similarity when vector mode is enabled - `negative_penalty` reflects hidden artworks and disliked tags - `repetition_penalty` suppresses creator and tag repetition inside one result page ### Trending contribution inside V2 V2 combines the artwork's stored trending columns like this: ```text trendingBoost = (trending_score_1h * weight_1h) + (trending_score_24h * weight_24h) + (trending_score_7d * weight_7d) ``` Then divides by 100 before merging into the final score. ### Negative signals V2 reads `user_negative_signals` and applies: - hidden artwork exclusion - disliked tag penalty ### Supporting data sources V2 reads from: - `user_recommendation_cache` - session/profile signal builders - `artworks` - `artwork_stats` - `artwork_similarities` - `artwork_embeddings` and vector service, when enabled - `user_followers` - `artwork_favourites` - `user_negative_signals` ## V1 pipeline V1 is simpler and category-affinity driven. It reads `user_interest_profiles`, then scores candidates with a weighted blend: ```text score = (w1 * affinity) + (w2 * recency) + (w3 * popularity) + (w4 * novelty) ``` Cold start falls back to a blend of popular artworks and `artwork_similarities` seeds. ## Background jobs and schedules ### Directly relevant - `RegenerateUserRecommendationCacheJob` - dispatched on demand when cache is missing or stale ### Support jobs for candidate quality - `RecBuildItemPairsFromFavouritesJob` every 4 hours - `RecComputeSimilarByTagsJob` daily at 02:00 - `RecComputeSimilarByBehaviorJob` daily at 02:15 - `RecComputeSimilarHybridJob` daily at 02:30 - `analytics:aggregate-discovery-feedback` daily at 03:25 These jobs do not directly render the page, but they improve the offline inputs and behavioral data used by the recommender. ## Cache behavior - V1 cache TTL default: 60 minutes - V2 cache TTL default: 15 minutes Cursor pagination is offset-based under the hood. ## Notes - `For You` is the most configuration-sensitive page in this set. - What a given user sees can differ by rollout bucket, `algo_version`, cache state, and whether V2/V3 features are enabled. - If you are debugging a single user's page, inspect `RecommendationFeedResolver::inspectDecision()` first.