#!/bin/bash # ============================================================================================== # ================================= DECISION ENGINE ============================================ # ============================================================================================== # Central behavior-driven decision kernel used by media automation systems. # # This engine does NOT download media. # This engine does NOT search indexers. # This engine does NOT manage applications directly. # # Instead: # It evaluates candidates. # Scores them against ecosystem behavior. # Applies adaptive filtering rules. # Returns decisions to consumer scripts. # # ============================================================================================== # ── DESIGN PHILOSOPHY ───────────────────────────────────────────────────────────────────────── # # The ecosystem is built around: # # Family-aware decisions # Time-aware weighting # Behavior-driven adaptation # Domain-specific strictness # # Each media domain consumes the engine differently: # # Lidarr → highly selective, quality-first discovery # Sonarr → balanced family-aware episodic intake # Radarr → broader flexibility with intelligent filtering # # The engine itself remains domain-agnostic. # Consumers define their own thresholds, weights, and strictness profiles. # # This separation prevents: # # Cross-domain bias pollution # Unified-feed degeneration # Overfitting to a single user's habits # Low-quality recommendation drift over time # # Result: # # Music stays curated and intentional # TV stays balanced across users # Movies remain adaptive without chaos # # ============================================================================================== # ── RESPONSIBILITIES ────────────────────────────────────────────────────────────────────────── # # The decision engine is responsible for: # # Candidate scoring # User weighting # Temporal decay # Popularity normalization # Duplicate prevention # Strictness enforcement # Threshold evaluation # Final decision output # # The engine returns: # # ACCEPT # REJECT # SCORE # REASON # # Consumer scripts decide what to do with the result. # # ============================================================================================== # ── ECOSYSTEM ROLE ──────────────────────────────────────────────────────────────────────────── # # Kernel Position: # # Kernel/ # ├── decision_engine.sh # ├── transcoding_engine.sh # ├── future_engine_modules... # # Shared reusable logic belongs in: # # common.sh # # Shared ecosystem configuration belongs in: # # master.conf # # Host-specific secrets/configuration belong in: # # master_host*.conf # # The kernel contains: # # Stateful logic # Adaptive systems # Scoring systems # Cross-domain intelligence # # ============================================================================================== # ── VERSION ─────────────────────────────────────────────────────────────────────────────────── # # v1.0 # Initial decision kernel architecture # Built first for Lidarr discovery orchestration # # ============================================================================================== # ============================================================================================== # ── SCORE CANDIDATE ─────────────────────────────────────────────────────────────────────────── # ============================================================================================== # Calculates weighted score for a media candidate. # # Inputs: # USER_SCORE # POPULARITY_SCORE # RECENCY_SCORE # QUALITY_SCORE # # Output: # TOTAL_SCORE # # Consumer scripts define actual weighting values. score_candidate() { local user_score="${1:-0}" local popularity_score="${2:-0}" local recency_score="${3:-0}" local quality_score="${4:-0}" TOTAL_SCORE=$(( \ user_score + \ popularity_score + \ recency_score + \ quality_score \ )) echo "$TOTAL_SCORE" } # ============================================================================================== # ── THRESHOLD CHECK ─────────────────────────────────────────────────────────────────────────── # ============================================================================================== # Determines if candidate passes scoring threshold. # # Usage: # evaluate_threshold "$score" "$minimum" evaluate_threshold() { local score="$1" local minimum="$2" if (( score >= minimum )); then return 0 fi return 1 } # ============================================================================================== # ── TEMPORAL DECAY ──────────────────────────────────────────────────────────────────────────── # ============================================================================================== # Reduces influence of old behavior over time. # # Prevents: # Permanent genre lock-in # Historical bias accumulation # Dead-user dominance # # Usage: # apply_temporal_decay current_score age_days apply_temporal_decay() { local score="$1" local age_days="$2" local decay=$(( age_days / 30 )) local adjusted=$(( score - decay )) (( adjusted < 0 )) && adjusted=0 echo "$adjusted" } # ============================================================================================== # ── DUPLICATE PROTECTION ────────────────────────────────────────────────────────────────────── # ============================================================================================== # Prevents repetitive acquisitions. # # Consumer defines: # cooldown periods # replay windows # duplicate tolerance # # Returns: # 0 = duplicate # 1 = unique is_duplicate_candidate() { local candidate="$1" local history_file="$2" grep -qi "^${candidate}$" "$history_file" 2>/dev/null } # ============================================================================================== # ── FINAL DECISION ──────────────────────────────────────────────────────────────────────────── # ============================================================================================== # Produces final engine verdict. # # Outputs: # ACCEPT # REJECT make_decision() { local score="$1" local threshold="$2" if evaluate_threshold "$score" "$threshold"; then echo "ACCEPT" else echo "REJECT" fi }