"""Hero (lead-story) emotional-safety guardrail. The single most prominent thing on the page must feel safe to encounter before coffee. A story can be objectively hopeful — a cancer breakthrough, say — and still be personally painful to meet unprompted at the top of the page. So the lead is chosen with a STRICTER filter than the rest of the brief: it must be very calm and must not mention grief/medical/violence terms. Such stories are still welcome among the five — they just don't lead by default. Per-user avoid-terms are applied to the brief before this runs, so a reader's personal boundaries always take precedence. """ from __future__ import annotations from .filters import text_matches_avoid_terms # Terms too emotionally charged to lead with, even inside a constructive story. HERO_GUARD_TERMS = [ "cancer", "glioblastoma", "tumor", "tumour", "carcinoma", "chemotherapy", "chemo", "death", "died", "dies", "dying", "dead", "fatal", "terminal", "deadly", "diagnosis", "diagnosed", "dementia", "alzheimer", "parkinson", "suicide", "murder", "killed", "killing", "war", "assault", "abuse", "overdose", "hospice", "palliative", "disease", ] HERO_MAX_CORTISOL = 1 HERO_MAX_RAGEBAIT = 1 def safe_to_lead(article: dict) -> bool: """True if an article is calm enough to be the page's lead.""" if (article.get("cortisol_score") or 0) > HERO_MAX_CORTISOL: return False if (article.get("ragebait_score") or 0) > HERO_MAX_RAGEBAIT: return False blob = f"{article.get('title') or ''} {article.get('description') or ''}" return not text_matches_avoid_terms(blob, HERO_GUARD_TERMS) def lead_with_gentle(items: list[dict]) -> list[dict]: """Reorder so the highest-ranked lead-safe item is first (the hero). The remaining items keep their ranked order. If nothing qualifies, the list is returned unchanged — the brief still needs a lead, and we never drop a constructive story, we only decline to make a charged one the first thing seen. """ for i, article in enumerate(items): if safe_to_lead(article): if i > 0: return [items[i], *items[:i], *items[i + 1:]] return items return items