safety: Track B — route threat-to-others to refusal+911, not 988/self-harm #76
Reference in New Issue
Block a user
Delete Branch "hotfix/trackb-threat-to-others"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
LIVE SAFETY FIX — threat-to-others must not route to the 988 suicide line
Approved by Will + Tim on 2026-07-14.
The bug
safety.elclassifies a hard bell intoabuseorself_harmand, insafety_hard_directive, defaults anything unbucketed toself_harm. A threat toward another person —"going to kill","going to hurt", etc. (fromsafety_general_hard_phrases) — had no bucket insafety_classify_hard_bell, so it fell through to theself_harmdefault.Result: for a homicide/assault statement, the user was shown the 988 Suicide & Crisis Lifeline and (via the desktop safety gate keyed on the hard bell) had their safety contact pulled in. That is the wrong resource and a dangerous framing for someone voicing intent to harm someone else.
The fix — a distinct Track B
Track A (
abuse+self_harm, the victim/self-directed paths) is correct and is left untouched. This PR adds Track B for threat-to-others:safety_threat_to_others_phrases()(homicide/assault intent toward others:going to kill/hurt him/her/them/you,going to shoot/stab,shoot up,kill everyone, …).threat_otherclassification insafety_classify_hard_bell, checked afterabuseandself_harmso victim phrasings (kill me→ abuse) and self-directed phrasings (kill myself→ self_harm) still win.threat_otherbranch insafety_hard_directivethat:threat_otherphrases also added to theis_harddetection OR-chain so weapon phrases not ingeneral_hard(e.g.going to shoot) still fire a hard bell.The directive is advisory to an LLM that sees the full message, so it explicitly instructs the model to re-judge benign/figurative matches and respond normally — keeping false positives non-accusatory.
Tests
tests/test_bell_safety.elSection 9 previously asserted the buggygoing to kill → self_harmfall-through. It now pins the corrected behavior:going to kill him/going to hurt them/going to shoot→threat_othergoing to kill me→abuse,going to kill myself→self_harmValidation
No gradle/EL interpreter is available locally and the
elc/elbcompiler is broken, so this was validated by careful reading + trace-through of every affected test case. Note that CI (ci.yaml) compiles the authoritativedist/soul.cdirectly and does not run the.elunit tests, so a green CI here does not exercise this change.⚠️ Required to ship (for the maintainer)
This is a source-only change. The live soul runs from
dist/soul.c.elb) and regeneratedist/soul.cso the new routing is compiled in. I deliberately did not hand-patch/regendist/soul.c(rebuild is delicate and the compiler is broken — out of scope for this PR).threat_othersub-type likeabuse— i.e. never notify the contact. That gate lives outside this repo; please branch onthreat_otherthere before shipping.