fix: ntfy ping is her personal text to Brian, by her decision — not a thought dump

Feedback: the push broadcast her raw internal thought ("Eelis Parssinen's
victory is a reminder...") — read like a journal entry, not her texting him.

Now the flow matches the intent: she thinks/journals, then *decides* "I should
tell Brian about this." think() asks for an optional `reach_out` — a real text
message addressed TO him in her own voice, written only when she chooses to. The
ping sends that message (title "Lyra", like a text from her), never the internal
thought. No reach_out = nothing sent (most thoughts stay hers).

- Pinging decoupled from the salience score: her decision (a reach_out) drives it,
  not a threshold. PING_SALIENCE is now an optional floor (default 0.0).
- Defensive: reject the placeholder echo ("reach_out"), too-short junk, or the
  thought pasted back as the message.
- notify.push: title now optional (omitted -> cleaner text-style notification).

Verified live: 3 passes kept private; a decided reach-out lands as a personal
text. Suite 67 green, ruff clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-22 01:39:11 +00:00
parent fef45b3e05
commit 43697f8340
4 changed files with 81 additions and 29 deletions
+38 -10
View File
@@ -226,28 +226,56 @@ def test_react_mode_makes_a_thread_about_a_feed_item(lyra, monkeypatch):
# --- proactive reach-out (ntfy) ------------------------------------------
def test_maybe_ping_gates_on_salience_and_records(lyra, monkeypatch):
def test_ping_sends_her_personal_message_when_she_reaches_out(lyra, monkeypatch):
_, th, box = lyra
monkeypatch.setenv("NTFY_URL", "http://ntfy.test")
monkeypatch.setenv("PING_QUIET_HOURS", "0-0") # disable quiet window for the test
sent = []
monkeypatch.setattr(th.notify, "push", lambda **k: (sent.append(k), True)[1])
_gen(box, title="big one", content="this really tugs", salience=0.9)
r = th.think(force_mode="new") # high salience -> should ping
assert len(sent) == 1 and "big one" in sent[0]["title"]
assert th.get_thread(r["thread_id"])["status"] == "surfaced" # ping marks it surfaced
assert th._meta_get("last_ping_at")
# high salience AND she wrote a personal note to Brian -> texts him that note
_gen(box, title="big one", content="internal thought, essay voice", salience=0.9,
reach_out="Hey — been thinking about you, got a sec?")
r = th.think(force_mode="new")
assert r["pinged"] is True
assert len(sent) == 1
assert sent[0]["message"] == "Hey — been thinking about you, got a sec?" # her words, not the thought
assert th.get_thread(r["thread_id"])["status"] == "surfaced" # ping marks it surfaced
def test_no_ping_without_a_reach_out_message(lyra, monkeypatch):
_, th, box = lyra
monkeypatch.setenv("NTFY_URL", "http://ntfy.test")
monkeypatch.setenv("PING_QUIET_HOURS", "0-0")
sent = []
monkeypatch.setattr(th.notify, "push", lambda **k: (sent.append(k), True)[1])
# salient thought but she did NOT decide to tell him -> no ping (it's not a broadcast)
_gen(box, content="a salient thought with no reach_out", salience=0.95)
assert th.think(force_mode="new")["pinged"] is False and sent == []
# the placeholder echo is rejected too (model copying the field name)
_gen(box, content="another", salience=0.95, reach_out="reach_out")
assert th.think(force_mode="new")["pinged"] is False and sent == []
def test_ping_salience_floor_is_optional(lyra, monkeypatch):
_, th, _ = lyra
monkeypatch.setenv("NTFY_URL", "http://ntfy.test")
monkeypatch.setenv("PING_QUIET_HOURS", "0-0")
sent = []
monkeypatch.setattr(th.notify, "push", lambda **k: (sent.append(k), True)[1])
# default floor 0.0 -> her decision (a message) is enough, any salience pings
assert th.maybe_ping(1, "hey, thinking of you", 0.2) is True
# but a floor can be set to suppress low-salience pings
sent.clear()
assert th.maybe_ping(r["thread_id"], "x", "quiet musing", 0.4) is False # below bar
assert sent == []
monkeypatch.setenv("PING_SALIENCE", "0.7")
assert th.maybe_ping(1, "hey", 0.4) is False
assert th.maybe_ping(1, "hey", 0.8) is True
def test_no_ping_without_ntfy(lyra, monkeypatch):
_, th, _ = lyra
sent = []
monkeypatch.setattr(th.notify, "push", lambda **k: (sent.append(k), True)[1])
# no NTFY_URL in env -> disabled regardless of salience
assert th.maybe_ping(1, "t", "c", 0.99) is False
# no NTFY_URL in env -> disabled even with a message + high salience
assert th.maybe_ping(1, "hey there", 0.99) is False
assert sent == []