Fix task looping: increase stale recovery to 6h, add file logging, use UNC paths

- Stale task recovery threshold 2h → 6h to prevent resetting tasks while Cora is still running
- Add rotating file logger (WARNING+) to logs/cheddahbot.log for debugging
- Silence httpx/httpcore INFO spam from terminal
- Switch watch folder paths from Z: drive letters to UNC paths to avoid intermittent mount drops
- Fix test_db tests to add messages so list_conversations includes them

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix/customer-field-migration
PeninsulaInd 2026-03-04 21:41:46 -06:00
parent 83c7c378e5
commit 45ab4c1b33
4 changed files with 36 additions and 10 deletions

View File

@ -1,6 +1,7 @@
"""Entry point: python -m cheddahbot"""
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path
from .agent import Agent
@ -15,6 +16,22 @@ logging.basicConfig(
format="%(asctime)s [%(name)s] %(levelname)s: %(message)s",
datefmt="%H:%M:%S",
)
# Warnings and errors to rotating log file
_log_dir = Path(__file__).resolve().parent.parent / "logs"
_log_dir.mkdir(exist_ok=True)
_file_handler = RotatingFileHandler(
_log_dir / "cheddahbot.log", maxBytes=5 * 1024 * 1024, backupCount=5
)
_file_handler.setLevel(logging.WARNING)
_file_handler.setFormatter(
logging.Formatter("%(asctime)s [%(name)s] %(levelname)s: %(message)s")
)
logging.getLogger().addHandler(_file_handler)
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("httpcore").setLevel(logging.WARNING)
log = logging.getLogger("cheddahbot")

View File

@ -271,7 +271,7 @@ class Scheduler:
return self._clickup_client
# Maximum time a task can stay in "automation underway" before recovery (seconds)
STALE_TASK_THRESHOLD_SECONDS = 2 * 60 * 60 # 2 hours
STALE_TASK_THRESHOLD_SECONDS = 6 * 60 * 60 # 6 hours
def _clickup_loop(self):
"""Poll ClickUp for tasks on a regular interval."""

View File

@ -92,7 +92,7 @@ clickup:
# Link Building settings
link_building:
blm_dir: "E:/dev/Big-Link-Man"
watch_folder: "Z:/cora-inbox"
watch_folder: "//PennQnap1/SHARE1/cora-inbox"
watch_interval_minutes: 60
default_branded_plus_ratio: 0.7
@ -104,12 +104,12 @@ autocora:
success_status: "running cora"
error_status: "error"
enabled: true
cora_human_inbox: "Z:/Cora-For-Human"
cora_human_inbox: "//PennQnap1/SHARE1/Cora-For-Human"
# Content creation settings
content:
cora_inbox: "Z:/content-cora-inbox"
outline_dir: "Z:/content-outlines"
cora_inbox: "//PennQnap1/SHARE1/content-cora-inbox"
outline_dir: "//PennQnap1/SHARE1/content-outlines"
# ntfy.sh push notifications
ntfy:

View File

@ -10,21 +10,27 @@ from cheddahbot.db import Database
class TestConversationsAgentName:
"""Conversations are tagged by agent_name for per-agent history filtering."""
def _add_msg(self, db, conv_id):
"""Add a dummy message so list_conversations() includes this conv."""
db.add_message(conv_id, "user", "hello")
def test_create_with_default_agent_name(self, tmp_db):
tmp_db.create_conversation("conv1")
self._add_msg(tmp_db, "conv1")
convs = tmp_db.list_conversations()
assert len(convs) == 1
assert convs[0]["agent_name"] == "default"
def test_create_with_custom_agent_name(self, tmp_db):
tmp_db.create_conversation("conv1", agent_name="writer")
self._add_msg(tmp_db, "conv1")
convs = tmp_db.list_conversations()
assert convs[0]["agent_name"] == "writer"
def test_list_filters_by_agent_name(self, tmp_db):
tmp_db.create_conversation("c1", agent_name="default")
tmp_db.create_conversation("c2", agent_name="writer")
tmp_db.create_conversation("c3", agent_name="default")
for cid, agent in [("c1", "default"), ("c2", "writer"), ("c3", "default")]:
tmp_db.create_conversation(cid, agent_name=agent)
self._add_msg(tmp_db, cid)
default_convs = tmp_db.list_conversations(agent_name="default")
writer_convs = tmp_db.list_conversations(agent_name="writer")
@ -35,14 +41,16 @@ class TestConversationsAgentName:
assert len(all_convs) == 3
def test_list_without_filter_returns_all(self, tmp_db):
tmp_db.create_conversation("c1", agent_name="a")
tmp_db.create_conversation("c2", agent_name="b")
for cid, agent in [("c1", "a"), ("c2", "b")]:
tmp_db.create_conversation(cid, agent_name=agent)
self._add_msg(tmp_db, cid)
convs = tmp_db.list_conversations()
assert len(convs) == 2
def test_list_returns_agent_name_in_results(self, tmp_db):
tmp_db.create_conversation("c1", agent_name="researcher")
self._add_msg(tmp_db, "c1")
convs = tmp_db.list_conversations()
assert "agent_name" in convs[0]
assert convs[0]["agent_name"] == "researcher"
@ -52,6 +60,7 @@ class TestConversationsAgentName:
db_path = tmp_path / "test_migrate.db"
db1 = Database(db_path)
db1.create_conversation("c1", agent_name="ops")
db1.add_message("c1", "user", "hello")
# Re-init on same DB file triggers migration again
db2 = Database(db_path)
convs = db2.list_conversations()