/** * Harmony Score Calculation * * Design intent: * - Influence adapts; people are not judged. * - Guidance replaces punishment. * - Fit emerges naturally. * * Philosophy: * - Score is private, decays over time, and is reversible. * - Never bans or removes beliefs. * - Shapes distribution width, not access. * * Inputs: * - Blocks received (pattern-based, not single incidents) * - Trusted reports (from high-harmony users) * - Category friction (posting to sensitive categories with low CIS) * - Posting cadence (erratic spikes vs steady participation) * - Rewrite prompts triggered (content rejected for tone) * - False reports made (reports that were dismissed) * * Effects: * - Shapes distribution width (reach) * - Adds gentle posting friction if low * - Limits Trending eligibility */ export interface HarmonyInputs { user_id: string; blocks_received_7d: number; blocks_received_30d: number; trusted_reports_against: number; total_reports_against: number; posts_rejected_7d: number; // rewrite prompts triggered posts_created_7d: number; false_reports_filed: number; // reports dismissed after review validated_reports_filed: number; // reports confirmed after review days_since_signup: number; current_harmony_score: number; current_tier: string; } export interface HarmonyAdjustment { new_score: number; delta: number; reason: string; new_tier: string; } /** * Calculate harmony score adjustment based on recent behavior */ export function calculateHarmonyAdjustment(inputs: HarmonyInputs): HarmonyAdjustment { let delta = 0; const reasons: string[] = []; // 1. Blocks received (pattern-based) // Single block = minor signal. Pattern of blocks = strong negative signal. if (inputs.blocks_received_7d >= 3) { delta -= 10; reasons.push('Multiple blocks received recently'); } else if (inputs.blocks_received_30d >= 5) { delta -= 5; reasons.push('Pattern of blocks over time'); } // 2. Trusted reports // Reports from high-harmony users are strong negative signals if (inputs.trusted_reports_against >= 2) { delta -= 8; reasons.push('Multiple reports from trusted users'); } else if (inputs.trusted_reports_against === 1) { delta -= 3; reasons.push('Report from trusted user'); } // 3. Content rejection rate (rewrite prompts) // High rejection rate indicates persistent tone issues const rejectionRate = inputs.posts_created_7d > 0 ? inputs.posts_rejected_7d / inputs.posts_created_7d : 0; if (rejectionRate > 0.3) { delta -= 6; reasons.push('High content rejection rate'); } else if (rejectionRate > 0.1) { delta -= 2; reasons.push('Some content rejected for tone'); } // 4. False reports filed // Filing false reports is harmful behavior if (inputs.false_reports_filed >= 3) { delta -= 7; reasons.push('Multiple false reports filed'); } else if (inputs.false_reports_filed >= 1) { delta -= 3; reasons.push('False report filed'); } // 5. Positive signals: Validated reports // Accurate reporting helps the community if (inputs.validated_reports_filed >= 3) { delta += 5; reasons.push('Helpful reporting behavior'); } else if (inputs.validated_reports_filed >= 1) { delta += 2; reasons.push('Validated report filed'); } // 6. Time-based trust growth // Steady participation without issues slowly builds trust if (inputs.days_since_signup > 90 && delta >= 0) { delta += 2; reasons.push('Sustained positive participation'); } else if (inputs.days_since_signup > 30 && delta >= 0) { delta += 1; reasons.push('Consistent participation'); } // 7. Natural decay toward equilibrium (50) // Scores gradually drift back toward 50 over time // This ensures old negative signals fade if (inputs.current_harmony_score < 45) { delta += 1; reasons.push('Natural recovery over time'); } else if (inputs.current_harmony_score > 60) { delta -= 1; reasons.push('Natural equilibrium adjustment'); } // 8. Calculate new score with bounds [0, 100] const new_score = Math.max(0, Math.min(100, inputs.current_harmony_score + delta)); // 9. Determine tier based on new score let new_tier: string; if (new_score >= 75) { new_tier = 'established'; } else if (new_score >= 50) { new_tier = 'trusted'; } else if (new_score >= 25) { new_tier = 'new'; } else { new_tier = 'restricted'; } return { new_score, delta, reason: reasons.join('; ') || 'No significant changes', new_tier, }; } /** * Get user-facing explanation of harmony score (without revealing the number) */ export function getHarmonyExplanation(tier: string, score: number): string { if (tier === 'restricted') { return 'Your posts currently have limited reach. This happens when content patterns trigger community concerns. Your reach will naturally restore over time with calm participation.'; } if (tier === 'new') { return 'Your posts reach a modest audience while you build trust. Steady participation and helpful contributions will gradually expand your reach.'; } if (tier === 'trusted') { return 'Your posts reach a good audience. You have shown consistent, calm participation.'; } if (tier === 'established') { return 'Your posts reach a wide audience. You have built strong trust through sustained positive contributions.'; } return 'Your reach is determined by your participation patterns and community response.'; } /** * Determine reach multiplier for feed algorithms */ export function getReachMultiplier(tier: string, score: number): number { const baseMultiplier: Record = { restricted: 0.2, new: 0.6, trusted: 1.0, established: 1.4, }; // Fine-tune based on score within tier const tierBase = baseMultiplier[tier] || 1.0; const scoreAdjustment = (score - 50) / 200; // -0.25 to +0.25 return Math.max(0.1, tierBase + scoreAdjustment); }