diff --git a/tests/test_push_triggers.py b/tests/test_push_triggers.py index 163b9c037..efa3f0cb3 100644 --- a/tests/test_push_triggers.py +++ b/tests/test_push_triggers.py @@ -392,3 +392,39 @@ def test_handle_weekly_reflection_finish_skips_when_file_never_appears( assert sleeps == [1] * 10 assert send_calls == [] assert chat_events == [] + + +def test_handle_weekly_reflection_finish_dedupes_chat_event_without_devices( + monkeypatch, tmp_path +): + monkeypatch.setenv("_SOLSTONE_JOURNAL_OVERRIDE", str(tmp_path)) + reflection_path = tmp_path / "reflections" / "weekly" / "20260308.md" + reflection_path.parent.mkdir(parents=True, exist_ok=True) + reflection_path.write_text("# reflection\n", encoding="utf-8") + monkeypatch.setattr(triggers.time, "sleep", lambda seconds: None) + monkeypatch.setattr(triggers, "_eligible_devices", lambda: []) + chat_events: list[dict[str, object]] = [] + monkeypatch.setattr(triggers, "read_chat_events", lambda day: list(chat_events)) + monkeypatch.setattr( + triggers, + "append_chat_event", + lambda kind, **fields: chat_events.append({"kind": kind, **fields}), + ) + + message = { + "tract": "cortex", + "event": "finish", + "name": "weekly_reflection", + "day": "20260308", + } + triggers.handle_weekly_reflection_finish(message) + triggers.handle_weekly_reflection_finish(message) + + assert chat_events == [ + { + "kind": "reflection_ready", + "day": "20260308", + "url": "/app/reflections/20260308", + } + ] + assert not _log_path(tmp_path).exists() diff --git a/think/push/triggers.py b/think/push/triggers.py index d22b99c0a..b838d4142 100644 --- a/think/push/triggers.py +++ b/think/push/triggers.py @@ -13,7 +13,7 @@ from pathlib import Path from typing import Any from apps.home.routes import _load_briefing_md -from convey.chat_stream import append_chat_event +from convey.chat_stream import append_chat_event, read_chat_events from think.activities import load_activity_records from think.facets import get_enabled_facets from think.push.config import get_bundle_id, get_environment, is_configured @@ -116,6 +116,16 @@ def _record_send( ) +def _has_reflection_ready_event(day: str, route: str) -> bool: + today = datetime.now().strftime("%Y%m%d") + return any( + event.get("kind") == "reflection_ready" + and event.get("day") == day + and event.get("url") == route + for event in read_chat_events(today) + ) + + def handle_briefing_finish(message: dict[str, Any]) -> None: if message.get("tract") != "cortex": return @@ -278,7 +288,8 @@ def handle_weekly_reflection_finish(message: dict[str, Any]) -> None: context_id=context_id, route=route, ) - append_chat_event("reflection_ready", day=day, url=route) + if not _has_reflection_ready_event(day, route): + append_chat_event("reflection_ready", day=day, url=route) __all__ = [