15d51fb8fd
Hero guardrail (core to the promise, not cosmetic): - New hero.py: the lead story is chosen with a stricter filter than the rest of the brief — very low cortisol/ragebait and no grief/medical/violence terms (cancer, glioblastoma, death, diagnosis, ...). Such constructive-but-charged stories stay among the five; they just never lead by default. - /api/brief applies user avoid-terms FIRST, then lead_with_gentle, so personal boundaries always take precedence over the general guardrail. - Verified live: the brief no longer leads with a glioblastoma story. Card polish (per review): - Secondary cards with no real image are now text-first (no row of empty media bands); hero still always shows media or a typographic fallback. - Inline tuning actions are quiet until hover/focus on pointer devices, and stay visible (softer) on touch — less interface machinery. Tests: hero safety + lead reordering (70 total). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
49 lines
1.9 KiB
Python
49 lines
1.9 KiB
Python
from goodnews.hero import lead_with_gentle, safe_to_lead
|
|
|
|
|
|
def art(title, desc="", cortisol=0, ragebait=0):
|
|
return {"title": title, "description": desc, "cortisol_score": cortisol, "ragebait_score": ragebait}
|
|
|
|
|
|
def test_calm_story_is_safe_to_lead():
|
|
assert safe_to_lead(art("Rare British plant returns from the brink"))
|
|
|
|
|
|
def test_medical_grief_terms_are_not_safe_to_lead():
|
|
assert not safe_to_lead(art("Nanofiber implant doubles survival in glioblastoma mice", "brain cancer"))
|
|
assert not safe_to_lead(art("New drug for a deadly disease"))
|
|
assert not safe_to_lead(art("A community remembers those who died"))
|
|
|
|
|
|
def test_high_cortisol_or_ragebait_not_safe():
|
|
assert not safe_to_lead(art("Calm-sounding title", cortisol=4))
|
|
assert not safe_to_lead(art("Calm-sounding title", ragebait=3))
|
|
|
|
|
|
def test_substring_safe_words_not_falsely_flagged():
|
|
# "scar" must not trip on "scared"? we don't list scar; ensure clean titles pass
|
|
assert safe_to_lead(art("Volunteers restore a coastal wetland"))
|
|
|
|
|
|
def test_lead_with_gentle_promotes_first_safe_item():
|
|
items = [
|
|
art("Cancer breakthrough in mice", "kill cancer cells"), # charged — must not lead
|
|
art("Tiny blue octopus found off the Galapagos"), # calm — should lead
|
|
art("Solar exports hit a record"),
|
|
]
|
|
out = lead_with_gentle(items)
|
|
assert out[0]["title"].startswith("Tiny blue octopus")
|
|
assert len(out) == 3
|
|
assert any("Cancer" in a["title"] for a in out) # still present, just not first
|
|
|
|
|
|
def test_lead_with_gentle_keeps_order_when_first_is_already_safe():
|
|
items = [art("A gentle discovery"), art("Cancer news")]
|
|
assert lead_with_gentle(items)[0]["title"] == "A gentle discovery"
|
|
|
|
|
|
def test_lead_with_gentle_unchanged_when_none_safe():
|
|
items = [art("Cancer story"), art("War deaths reported", cortisol=8)]
|
|
out = lead_with_gentle(items)
|
|
assert out[0]["title"] == "Cancer story" # unchanged; brief still needs a lead
|