From d4e2ebb99f2b8cc0031dc431a2d4f11172a4d63b Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 19 Feb 2026 16:27:02 +0200 Subject: [PATCH] fix(hitl): rollback on KB delete failure, tighten linear tool guards - delete_linear_issue + delete_notion_page: add db_session.rollback() in the KB document deletion exception handler so the session is never left in a PendingRollbackError state after a failed commit, which would otherwise break all subsequent DB operations in the same request - delete_linear_issue: include issue identifier (e.g. ENG-42) in the success message so the caller can confirm which issue was archived - update_linear_issue: remove redundant label_ids ternary (x if x is not None else None -> x) - create_linear_issue: add logger.error on empty-title guard for parity with the equivalent Notion tool --- .../app/agents/new_chat/tools/linear/create_issue.py | 1 + .../app/agents/new_chat/tools/linear/delete_issue.py | 4 ++++ .../app/agents/new_chat/tools/linear/update_issue.py | 4 +--- .../app/agents/new_chat/tools/notion/delete_page.py | 3 +-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/surfsense_backend/app/agents/new_chat/tools/linear/create_issue.py b/surfsense_backend/app/agents/new_chat/tools/linear/create_issue.py index 319151d59..b86119836 100644 --- a/surfsense_backend/app/agents/new_chat/tools/linear/create_issue.py +++ b/surfsense_backend/app/agents/new_chat/tools/linear/create_issue.py @@ -144,6 +144,7 @@ def create_create_linear_issue_tool( final_connector_id = final_params.get("connector_id", connector_id) if not final_title or not final_title.strip(): + logger.error("Title is empty or contains only whitespace") return {"status": "error", "message": "Issue title cannot be empty."} if not final_team_id: return { diff --git a/surfsense_backend/app/agents/new_chat/tools/linear/delete_issue.py b/surfsense_backend/app/agents/new_chat/tools/linear/delete_issue.py index cece07b8b..361603d05 100644 --- a/surfsense_backend/app/agents/new_chat/tools/linear/delete_issue.py +++ b/surfsense_backend/app/agents/new_chat/tools/linear/delete_issue.py @@ -99,6 +99,7 @@ def create_delete_linear_issue_tool( return {"status": "error", "message": error_msg} issue_id = context["issue"]["id"] + issue_identifier = context["issue"].get("identifier", "") document_id = context["issue"]["document_id"] connector_id_from_context = context.get("workspace", {}).get("id") @@ -229,12 +230,15 @@ def create_delete_linear_issue_tool( logger.warning(f"Document {document_id} not found in KB") except Exception as e: logger.error(f"Failed to delete document from KB: {e}") + await db_session.rollback() result["warning"] = ( f"Issue archived in Linear, but failed to remove from knowledge base: {e!s}" ) if result.get("status") == "success": result["deleted_from_kb"] = deleted_from_kb + if issue_identifier: + result["message"] = f"Issue {issue_identifier} archived successfully." if deleted_from_kb: result["message"] = ( f"{result.get('message', '')} Also removed from the knowledge base." diff --git a/surfsense_backend/app/agents/new_chat/tools/linear/update_issue.py b/surfsense_backend/app/agents/new_chat/tools/linear/update_issue.py index 89e5dc2c0..f67cf9472 100644 --- a/surfsense_backend/app/agents/new_chat/tools/linear/update_issue.py +++ b/surfsense_backend/app/agents/new_chat/tools/linear/update_issue.py @@ -232,9 +232,7 @@ def create_update_linear_issue_tool( state_id=final_new_state_id, assignee_id=final_new_assignee_id, priority=final_new_priority, - label_ids=final_new_label_ids - if final_new_label_ids is not None - else None, + label_ids=final_new_label_ids, ) if updated_issue.get("status") == "error": diff --git a/surfsense_backend/app/agents/new_chat/tools/notion/delete_page.py b/surfsense_backend/app/agents/new_chat/tools/notion/delete_page.py index 37e7ee5f3..65628a38d 100644 --- a/surfsense_backend/app/agents/new_chat/tools/notion/delete_page.py +++ b/surfsense_backend/app/agents/new_chat/tools/notion/delete_page.py @@ -240,8 +240,7 @@ def create_delete_notion_page_tool( logger.warning(f"Document {document_id} not found in KB") except Exception as e: logger.error(f"Failed to delete document from KB: {e}") - # Don't fail the whole operation if KB deletion fails - # The page is already deleted from Notion, so inform the user + await db_session.rollback() result["warning"] = ( f"Page deleted from Notion, but failed to remove from knowledge base: {e!s}" )