Fix missing ClickUp comments on error status transitions and add pipeline error audit log
- File handler now captures DEBUG+ (was WARNING) for full pipeline visibility - Add ClickUp comments at every error status transition (missing IMSURL, pipeline crash, content crash) - Content watcher exception now also sets error status (was silently failing) - Cora distributor resets matched error tasks to "running cora" when new XLSX arrives - Add dedicated logs/pipeline_errors.log for tool-handled errors (audit trail) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>master
parent
e8df7a9750
commit
b857d3cb8c
|
|
@ -17,13 +17,13 @@ logging.basicConfig(
|
|||
datefmt="%H:%M:%S",
|
||||
)
|
||||
|
||||
# Warnings and errors to rotating log file
|
||||
# All levels to rotating log file (DEBUG+)
|
||||
_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.setLevel(logging.DEBUG)
|
||||
_file_handler.setFormatter(
|
||||
logging.Formatter("%(asctime)s [%(name)s] %(levelname)s: %(message)s")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,19 @@ if TYPE_CHECKING:
|
|||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Dedicated logger for "tool returned error but likely handled it" cases.
|
||||
# Writes to logs/pipeline_errors.log for manual review.
|
||||
_pipeline_err_log = logging.getLogger("cheddahbot.pipeline_errors")
|
||||
_pipeline_err_log.propagate = False
|
||||
_pe_dir = Path(__file__).resolve().parent.parent / "logs"
|
||||
_pe_dir.mkdir(exist_ok=True)
|
||||
_pe_handler = logging.FileHandler(_pe_dir / "pipeline_errors.log", encoding="utf-8")
|
||||
_pe_handler.setFormatter(
|
||||
logging.Formatter("%(asctime)s | %(message)s")
|
||||
)
|
||||
_pipeline_err_log.addHandler(_pe_handler)
|
||||
_pipeline_err_log.setLevel(logging.INFO)
|
||||
|
||||
HEARTBEAT_OK = "HEARTBEAT_OK"
|
||||
|
||||
# Only tasks in these statuses are eligible for xlsx → ClickUp matching.
|
||||
|
|
@ -789,6 +802,11 @@ class Scheduler:
|
|||
money_site_url = matched_task.custom_fields.get("IMSURL", "") or ""
|
||||
if not money_site_url:
|
||||
log.warning("Task %s (%s) missing IMSURL — skipping", task_id, matched_task.name)
|
||||
client.add_comment(
|
||||
task_id,
|
||||
"❌ Link building skipped — IMSURL field is empty. "
|
||||
"Set the IMSURL field in ClickUp so the pipeline knows where to build links.",
|
||||
)
|
||||
client.update_task_status(task_id, self.config.clickup.error_status)
|
||||
self._notify(
|
||||
f"Folder watcher: **{filename}** matched task **{matched_task.name}** "
|
||||
|
|
@ -823,6 +841,10 @@ class Scheduler:
|
|||
|
||||
if "Error" in result and "## Step" not in result:
|
||||
# Pipeline failed — tool handles its own ClickUp error status
|
||||
_pipeline_err_log.info(
|
||||
"LINKBUILDING | task=%s | file=%s | result=%s",
|
||||
task_id, filename, result[:500],
|
||||
)
|
||||
self._notify(
|
||||
f"Folder watcher: pipeline **failed** for **{filename}**.\n"
|
||||
f"Error: {result[:200]}",
|
||||
|
|
@ -847,6 +869,10 @@ class Scheduler:
|
|||
|
||||
except Exception as e:
|
||||
log.error("Folder watcher pipeline error for %s: %s", filename, e)
|
||||
client.add_comment(
|
||||
task_id,
|
||||
f"❌ Link building pipeline crashed.\n\nError: {str(e)[:2000]}",
|
||||
)
|
||||
client.update_task_status(task_id, self.config.clickup.error_status)
|
||||
finally:
|
||||
self._unregister_execution(task_id)
|
||||
|
|
@ -990,6 +1016,10 @@ class Scheduler:
|
|||
result = "Error: tool registry not available"
|
||||
|
||||
if result.startswith("Error:"):
|
||||
_pipeline_err_log.info(
|
||||
"CONTENT | task=%s | file=%s | result=%s",
|
||||
task_id, filename, result[:500],
|
||||
)
|
||||
self._notify(
|
||||
f"Content watcher: pipeline **failed** for **{filename}**.\n"
|
||||
f"Error: {result[:200]}",
|
||||
|
|
@ -1014,6 +1044,11 @@ class Scheduler:
|
|||
|
||||
except Exception as e:
|
||||
log.error("Content watcher pipeline error for %s: %s", filename, e)
|
||||
client.add_comment(
|
||||
task_id,
|
||||
f"❌ Content pipeline crashed.\n\nError: {str(e)[:2000]}",
|
||||
)
|
||||
client.update_task_status(task_id, self.config.clickup.error_status)
|
||||
finally:
|
||||
self._unregister_execution(task_id)
|
||||
|
||||
|
|
@ -1125,6 +1160,7 @@ class Scheduler:
|
|||
has_lb = False
|
||||
has_content = False
|
||||
matched_names = []
|
||||
matched_error_tasks = []
|
||||
|
||||
for task in tasks:
|
||||
if task.status not in _CORA_ELIGIBLE_STATUSES:
|
||||
|
|
@ -1138,6 +1174,8 @@ class Scheduler:
|
|||
continue
|
||||
|
||||
matched_names.append(task.name)
|
||||
if task.status == self.config.clickup.error_status:
|
||||
matched_error_tasks.append(task)
|
||||
if task.task_type == "Link Building":
|
||||
has_lb = True
|
||||
elif task.task_type in ("Content Creation", "On Page Optimization"):
|
||||
|
|
@ -1174,6 +1212,19 @@ class Scheduler:
|
|||
)
|
||||
return
|
||||
|
||||
# Reset any matched tasks that were in "error" back to "running cora"
|
||||
# so the pipeline picks them up again.
|
||||
for task in matched_error_tasks:
|
||||
try:
|
||||
client.update_task_status(task.id, "running cora")
|
||||
client.add_comment(
|
||||
task.id,
|
||||
f"New Cora XLSX distributed — resetting from error to running cora.",
|
||||
)
|
||||
log.info("Distributor: reset task %s (%s) from error → running cora", task.id, task.name)
|
||||
except Exception as e:
|
||||
log.warning("Distributor: failed to reset task %s: %s", task.id, e)
|
||||
|
||||
# Move original to processed/
|
||||
processed_dir = xlsx_path.parent / "processed"
|
||||
processed_dir.mkdir(exist_ok=True)
|
||||
|
|
|
|||
Loading…
Reference in New Issue