From 3f2798d338c8d99099b4a5066dd78219fc7578a9 Mon Sep 17 00:00:00 2001 From: PeninsulaInd Date: Fri, 20 Feb 2026 20:36:58 -0600 Subject: [PATCH] Add "automation underway" and "error" ClickUp statuses for bot visibility Tasks now show "automation underway" when the bot picks them up and "error" on failure, replacing the old "in progress" / "to do" fallbacks that were invisible on Bryan's ClickUp board. Folder watcher also syncs ClickUp status on match, missing IMSURL, pipeline failure, success, and exceptions. Co-Authored-By: Claude Opus 4.6 --- cheddahbot/config.py | 2 ++ cheddahbot/scheduler.py | 22 ++++++++++++++++------ cheddahbot/tools/linkbuilding.py | 8 +++----- cheddahbot/tools/press_release.py | 8 ++++---- config.yaml | 4 +++- tests/test_scheduler.py | 8 +++++--- 6 files changed, 33 insertions(+), 19 deletions(-) diff --git a/cheddahbot/config.py b/cheddahbot/config.py index 742483a..639f8d3 100644 --- a/cheddahbot/config.py +++ b/cheddahbot/config.py @@ -44,6 +44,8 @@ class ClickUpConfig: poll_statuses: list[str] = field(default_factory=lambda: ["to do"]) review_status: str = "internal review" in_progress_status: str = "in progress" + automation_status: str = "automation underway" + error_status: str = "error" task_type_field_name: str = "Work Category" default_auto_execute: bool = False skill_map: dict = field(default_factory=dict) diff --git a/cheddahbot/scheduler.py b/cheddahbot/scheduler.py index 0a31af4..cd2745a 100644 --- a/cheddahbot/scheduler.py +++ b/cheddahbot/scheduler.py @@ -339,8 +339,8 @@ class Scheduler: "custom_fields": task.custom_fields, } - # Move to "in progress" on ClickUp immediately - client.update_task_status(task_id, self.config.clickup.in_progress_status) + # Move to "automation underway" on ClickUp immediately + client.update_task_status(task_id, self.config.clickup.automation_status) self.db.kv_set(kv_key, json.dumps(state)) log.info("Executing ClickUp task: %s → %s", task.name, tool_name) @@ -371,8 +371,8 @@ class Scheduler: task_id, f"⚠️ CheddahBot could not execute this task.\n\n{result[:2000]}", ) - # Move back to "to do" so it can be retried - client.update_task_status(task_id, "to do") + # Move to "error" so Bryan can see what happened + client.update_task_status(task_id, self.config.clickup.error_status) self._notify( f"ClickUp task skipped: **{task.name}**\n" @@ -428,8 +428,8 @@ class Scheduler: client.add_comment( task_id, f"❌ CheddahBot failed to complete this task.\n\nError: {str(e)[:2000]}" ) - # Move back to "to do" so it can be retried after reset - client.update_task_status(task_id, "to do") + # Move to "error" so Bryan can see what happened + client.update_task_status(task_id, self.config.clickup.error_status) self._notify( f"ClickUp task failed: **{task.name}**\n" @@ -547,6 +547,11 @@ class Scheduler: # Extract tool args from matched task task_id = matched_task.id log.info("Matched '%s' to ClickUp task %s (%s)", filename, task_id, matched_task.name) + + # Set ClickUp status to "automation underway" + client = self._get_clickup_client() + client.update_task_status(task_id, self.config.clickup.automation_status) + self._notify( f"Folder watcher: matched **{filename}** to ClickUp task **{matched_task.name}**.\n" f"Starting Cora Backlinks pipeline...", @@ -570,6 +575,8 @@ class Scheduler: } ), ) + # Set ClickUp status to "error" so it's visible on the board + client.update_task_status(task_id, self.config.clickup.error_status) self._notify( f"Folder watcher: **{filename}** matched task **{matched_task.name}** " f"but **IMSURL is empty**. Set the IMSURL field in ClickUp before " @@ -614,6 +621,7 @@ class Scheduler: } ), ) + client.update_task_status(task_id, self.config.clickup.error_status) self._notify( f"Folder watcher: pipeline **failed** for **{filename}**.\n" f"Error: {result[:200]}", @@ -641,6 +649,7 @@ class Scheduler: } ), ) + client.update_task_status(task_id, self.config.clickup.review_status) self._notify( f"Folder watcher: pipeline **completed** for **{filename}**.\n" f"ClickUp task: {matched_task.name}", @@ -661,6 +670,7 @@ class Scheduler: } ), ) + client.update_task_status(task_id, self.config.clickup.error_status) def _match_xlsx_to_clickup(self, normalized_stem: str): """Find a ClickUp Link Building task whose Keyword matches the file stem. diff --git a/cheddahbot/tools/linkbuilding.py b/cheddahbot/tools/linkbuilding.py index 6042664..045b4dc 100644 --- a/cheddahbot/tools/linkbuilding.py +++ b/cheddahbot/tools/linkbuilding.py @@ -275,11 +275,11 @@ def _find_clickup_task(ctx: dict, keyword: str) -> str: if db: db.kv_set(f"clickup:task:{task_id}:state", json.dumps(state)) - # Move to "in progress" + # Move to "automation underway" cu_client2 = _get_clickup_client(ctx) if cu_client2: try: - cu_client2.update_task_status(task_id, config.clickup.in_progress_status) + cu_client2.update_task_status(task_id, config.clickup.automation_status) except Exception as e: log.warning("Failed to update ClickUp status for %s: %s", task_id, e) finally: @@ -361,9 +361,7 @@ def _fail_clickup_task(ctx: dict | None, task_id: str, error_msg: str) -> None: return config = ctx.get("config") - skill_map = config.clickup.skill_map if config else {} - lb_map = skill_map.get("Link Building", {}) - error_status = lb_map.get("error_status", "internal review") + error_status = config.clickup.error_status if config else "error" db = ctx.get("db") if db: diff --git a/cheddahbot/tools/press_release.py b/cheddahbot/tools/press_release.py index 921fbc2..64b19e5 100644 --- a/cheddahbot/tools/press_release.py +++ b/cheddahbot/tools/press_release.py @@ -116,11 +116,11 @@ def _find_clickup_task(ctx: dict, company_name: str) -> str: if db: db.kv_set(f"clickup:task:{task_id}:state", json.dumps(state)) - # Move to "in progress" on ClickUp + # Move to "automation underway" on ClickUp cu_client2 = _get_clickup_client(ctx) if cu_client2: try: - cu_client2.update_task_status(task_id, config.clickup.in_progress_status) + cu_client2.update_task_status(task_id, config.clickup.automation_status) except Exception as e: log.warning("Failed to update ClickUp status for %s: %s", task_id, e) finally: @@ -516,13 +516,13 @@ def write_press_releases( if cu_client: try: config = ctx["config"] - cu_client.update_task_status(clickup_task_id, config.clickup.in_progress_status) + cu_client.update_task_status(clickup_task_id, config.clickup.automation_status) cu_client.add_comment( clickup_task_id, f"🔄 CheddahBot starting press release creation.\n\n" f"Topic: {topic}\nCompany: {company_name}", ) - log.info("ClickUp task %s set to in-progress", clickup_task_id) + log.info("ClickUp task %s set to automation-underway", clickup_task_id) except Exception as e: log.warning("ClickUp start-sync failed for %s: %s", clickup_task_id, e) diff --git a/config.yaml b/config.yaml index 21435fb..d773416 100644 --- a/config.yaml +++ b/config.yaml @@ -45,6 +45,8 @@ clickup: poll_statuses: ["to do"] review_status: "internal review" in_progress_status: "in progress" + automation_status: "automation underway" + error_status: "error" task_type_field_name: "Work Category" default_auto_execute: false skill_map: @@ -60,7 +62,7 @@ clickup: tool: "run_link_building" auto_execute: false complete_status: "complete" - error_status: "internal review" + error_status: "error" field_mapping: lb_method: "LB Method" project_name: "task_name" diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py index 1df3c8e..47e1220 100644 --- a/tests/test_scheduler.py +++ b/tests/test_scheduler.py @@ -32,6 +32,8 @@ class _FakeClickUpConfig: poll_statuses: list[str] = field(default_factory=lambda: ["to do"]) review_status: str = "internal review" in_progress_status: str = "in progress" + automation_status: str = "automation underway" + error_status: str = "error" task_type_field_name: str = "Work Category" default_auto_execute: bool = True skill_map: dict = field(default_factory=lambda: {"Press Release": _PR_MAPPING}) @@ -220,7 +222,7 @@ class TestExecuteTask: mock_client.update_task_status.assert_any_call( "t1", - "in progress", + "automation underway", ) raw = tmp_db.kv_get("clickup:task:t1:state") @@ -266,7 +268,7 @@ class TestExecuteTask: assert "output/pr.docx" in state["deliverable_paths"] def test_failure_flow(self, tmp_db): - """Failed: state=failed, error comment, back to 'to do'.""" + """Failed: state=failed, error comment, status set to 'error'.""" config = _FakeConfig() agent = MagicMock() agent._tools = MagicMock() @@ -288,7 +290,7 @@ class TestExecuteTask: ) scheduler._execute_task(task) - mock_client.update_task_status.assert_any_call("t1", "to do") + mock_client.update_task_status.assert_any_call("t1", "error") mock_client.add_comment.assert_called_once() comment_text = mock_client.add_comment.call_args[0][1] assert "failed" in comment_text.lower()