From 9d06eaf57a0befb4051d5c14036e02a3d316fd69 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 26 Apr 2026 21:49:17 -0400 Subject: [PATCH] fix: log swallowed exceptions in detect_threads try/except (T80.3) The broad ``except Exception`` around detect_threads silently dropped programmer errors (wrong kwargs, import-time failures, etc), making diagnostics painful. Log at DEBUG with full exc_info so the failure surfaces in local logs without breaking the close pipeline's failure-tolerant contract. Adds test_detect_threads_failure_is_logged using caplog. --- chat/services/scene_summarize.py | 9 +++++- tests/test_per_pov_summary.py | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/chat/services/scene_summarize.py b/chat/services/scene_summarize.py index b6f3d09..d72ae9b 100644 --- a/chat/services/scene_summarize.py +++ b/chat/services/scene_summarize.py @@ -29,6 +29,7 @@ keeps moving. from __future__ import annotations import json +import logging import uuid from datetime import datetime, timezone from sqlite3 import Connection @@ -39,6 +40,8 @@ from chat.eventlog.log import append_and_apply from chat.llm.classify import classify from chat.llm.client import LLMClient +_log = logging.getLogger(__name__) + class ScenePOVSummary(BaseModel): """Classifier output: one witness's view of a closing scene. @@ -589,7 +592,11 @@ async def apply_scene_close_summary( open_threads=list_open_threads(conn, chat_id), timeout_s=timeout_s, ) - except Exception: + except Exception as exc: + # T80.3: log the swallowed exception at DEBUG so a + # programmer-error flap (e.g. wrong kwarg name) surfaces in + # local logs without breaking the close pipeline. + _log.debug("detect_threads failed: %s", exc, exc_info=True) from chat.services.thread_detection import ThreadDetectionResult thread_result = ThreadDetectionResult() diff --git a/tests/test_per_pov_summary.py b/tests/test_per_pov_summary.py index 593f696..ae4d35d 100644 --- a/tests/test_per_pov_summary.py +++ b/tests/test_per_pov_summary.py @@ -1625,3 +1625,51 @@ async def test_thread_detection_uses_scene_scoped_transcript( joined = " ".join(t.get("text", "") for t in scene_two_transcript) assert "SCENE_TWO" in joined assert "SCENE_ONE" not in joined + + +@pytest.mark.asyncio +async def test_detect_threads_failure_is_logged(tmp_path, monkeypatch, caplog): + """T80.3: when ``detect_threads`` raises, the broad except must log + the failure at DEBUG so a programmer-error flap surfaces in local + logs even though the close pipeline keeps moving.""" + import logging + + from chat.services import thread_detection as td_mod + + canned = json.dumps( + { + "summary": "BotA had a quick chat.", + "knowledge_facts": [], + "relationship_summary": "Steady.", + } + ) + + async def boom(client, **kwargs): + raise RuntimeError("test-detect-threads-boom") + + monkeypatch.setattr(td_mod, "detect_threads", boom) + + db = tmp_path / "t.db" + apply_migrations(db) + with open_db(db) as conn: + _seed_single_bot_scene(conn) + project(conn) + + caplog.set_level(logging.DEBUG, logger="chat.services.scene_summarize") + client = MockLLMClient(canned=[canned]) + # Close should NOT raise even though detect_threads did. + await apply_scene_close_summary( + conn, + client, + classifier_model="x", + chat_id="chat_bot_a", + scene_id=1, + host_bot_id="bot_a", + ) + + # Log carries the error message. + assert any( + "detect_threads failed" in rec.message + and "test-detect-threads-boom" in rec.message + for rec in caplog.records + ), [r.message for r in caplog.records]