'rank_v1', // ── Engagement signal weights (log-scaled) ────────────────────────────── 'weights' => [ 'views' => 1.0, 'favourites' => 3.0, 'downloads' => 2.5, ], // ── Time-decay half-lives (hours) ─────────────────────────────────────── 'half_life' => [ 'trending' => 72, // Explore / global trending 'new_hot' => 36, // New & Hot novelty feed 'best' => 720, // Evergreen / Best-of (30 days) 'category' => 96, // Per-category trending ], // ── Novelty boost (New & Hot only) ────────────────────────────────────── 'novelty_weight' => 0.35, // ── Quality modifiers ─────────────────────────────────────────────────── 'quality' => [ 'has_tags' => 0.05, 'has_thumbnail' => 0.02, 'tag_count_max' => 10, 'tag_count_bonus' => 0.01, // per normalised tag fraction (max 0.01 total) 'penalty_hidden' => 0.50, // deducted if hidden/inactive ], // ── Diversity constraints ──────────────────────────────────────────────── 'diversity' => [ 'max_per_author' => 3, 'list_size' => 50, 'candidate_pool' => 200, // top N candidates to run diversity filter on ], // ── Anti-spam / burst-view damping ────────────────────────────────────── 'spam' => [ 'views_24h_threshold' => 2000, 'fav_ratio_threshold' => 0.002, 'dl_ratio_threshold' => 0.001, 'trending_penalty_factor' => 0.5, ], // ── Redis cache ───────────────────────────────────────────────────────── 'cache' => [ 'ttl' => 900, // seconds (15 min) — lists are rebuilt hourly 'prefix' => 'rank', ], ];