From 69b51f8fa2af0abf717daaba4dca1c627023d82d Mon Sep 17 00:00:00 2001 From: Chad Date: Thu, 23 Jul 2026 17:43:17 -0500 Subject: [PATCH] feat(caretaker-triage): post comment before auto-closing issues (#28411) --- .../cloudrun/triage-worker/main.py | 23 ++++++++++- .../tests/test_integration_main.py | 5 ++- .../cloudrun/triage-worker/tests/test_main.py | 40 ++++++++++++------- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/tools/caretaker-agent/cloudrun/triage-worker/main.py b/tools/caretaker-agent/cloudrun/triage-worker/main.py index ce5bd1e391..87ef3823c5 100644 --- a/tools/caretaker-agent/cloudrun/triage-worker/main.py +++ b/tools/caretaker-agent/cloudrun/triage-worker/main.py @@ -9,6 +9,21 @@ from utils.validator import validate_triage_result from utils.egress import send_label_action, send_comment_action from db.issues_store import IssuesStore, ClaimAction, ReleaseAction +FEATURE_CLOSED_COMMENT = ( + "Thank you for bringing this to our attention. Right now, our " + "engineering team is focusing all resources on critical system " + "maintenance and core stability. Because of this, we don't have " + "immediate plans to address this specific issue. If you believe " + "this issue was misclassified, feel free to reopen it." +) + +QUALITY_CLOSED_COMMENT = ( + "Thank you for reaching out. We are closing this issue as it does " + "not contain a discernible description or actionable bug report for " + "our team to investigate. If you believe this was closed in error, " + "please feel free to open a new issue with complete reproduction details." +) + def main() -> None: """ @@ -84,7 +99,13 @@ def main() -> None: workable_spec = triage_result.get("workable_spec", {}) if quality in ["SPAM", "EMPTY", "FEATURE"]: - print(f"[WORKER] Quality: {quality}. Applying auto-close label.") + print(f"[WORKER] Quality: {quality}. Leaving comment and applying auto-close label.") + if quality == "FEATURE": + comment = FEATURE_CLOSED_COMMENT + else: # SPAM or EMPTY + comment = QUALITY_CLOSED_COMMENT + + send_comment_action(owner, repo, issue_number, comment) send_label_action(owner, repo, issue_number, ["auto-close"]) store.release_lock( owner, diff --git a/tools/caretaker-agent/cloudrun/triage-worker/tests/test_integration_main.py b/tools/caretaker-agent/cloudrun/triage-worker/tests/test_integration_main.py index dc38e3af07..b12757edd7 100644 --- a/tools/caretaker-agent/cloudrun/triage-worker/tests/test_integration_main.py +++ b/tools/caretaker-agent/cloudrun/triage-worker/tests/test_integration_main.py @@ -214,13 +214,15 @@ class TestIntegrationMain(unittest.TestCase): self.assertIsNone(self.stored_data["lock"]["holder"]) @patch("main.process_issue_triage") + @patch("main.send_comment_action") @patch("main.send_label_action") - def test_auto_close_flows(self, mock_send_label, mock_triage): + def test_auto_close_flows(self, mock_send_label, mock_send_comment, mock_triage): """Verifies end-to-end flow for auto-closed issues.""" for quality in ["SPAM", "EMPTY", "FEATURE"]: self.mock_store.acquire_lock.reset_mock() self.mock_store.release_lock.reset_mock() mock_send_label.reset_mock() + mock_send_comment.reset_mock() mock_triage.reset_mock() self.stored_data = { @@ -249,6 +251,7 @@ class TestIntegrationMain(unittest.TestCase): mock_send_label.assert_called_once_with( "owner", "repo", 42, ["auto-close"] ) + mock_send_comment.assert_called_once() self.assertEqual(self.stored_data["status"], "AUTO_CLOSE") self.assertIsNone(self.stored_data["lock"]["holder"]) diff --git a/tools/caretaker-agent/cloudrun/triage-worker/tests/test_main.py b/tools/caretaker-agent/cloudrun/triage-worker/tests/test_main.py index 34c3dd0488..e255ae8130 100644 --- a/tools/caretaker-agent/cloudrun/triage-worker/tests/test_main.py +++ b/tools/caretaker-agent/cloudrun/triage-worker/tests/test_main.py @@ -79,23 +79,35 @@ class TestMainExecutionLoop(unittest.TestCase): self.assertEqual(ctx.exception.code, 0) @patch("main.process_issue_triage") + @patch("main.send_comment_action") @patch("main.send_label_action") - def test_main_auto_close_quality_flow(self, mock_send_label, mock_triage): - """SPAM/EMPTY/FEATURE issues dispatch auto-close label.""" - self.mock_store.acquire_lock.return_value = ClaimAction.PROCEED - output = json.dumps({"triage_metadata": {"quality": "SPAM"}}) - mock_triage.return_value = (True, output) + def test_main_auto_close_quality_flow( + self, mock_send_label, mock_send_comment, mock_triage + ): + """SPAM, EMPTY, and FEATURE issues dispatch comment and auto-close label.""" + for quality in ["SPAM", "EMPTY", "FEATURE"]: + with self.subTest(quality=quality): + self.mock_store.acquire_lock.reset_mock() + self.mock_store.release_lock.reset_mock() + mock_send_label.reset_mock() + mock_send_comment.reset_mock() + mock_triage.reset_mock() - with self.assertRaises(SystemExit) as ctx: - main() + self.mock_store.acquire_lock.return_value = ClaimAction.PROCEED + output = json.dumps({"triage_metadata": {"quality": quality}}) + mock_triage.return_value = (True, output) - self.assertEqual(ctx.exception.code, 0) - mock_send_label.assert_called_once_with( - "owner", "repo", 42, ["auto-close"] - ) - self.mock_store.release_lock.assert_called_once_with( - "owner", "repo", 42, "exec-123", success=True, status="AUTO_CLOSE" - ) + with self.assertRaises(SystemExit) as ctx: + main() + + self.assertEqual(ctx.exception.code, 0) + mock_send_comment.assert_called_once() + mock_send_label.assert_called_once_with( + "owner", "repo", 42, ["auto-close"] + ) + self.mock_store.release_lock.assert_called_once_with( + "owner", "repo", 42, "exec-123", success=True, status="AUTO_CLOSE" + ) @patch("main.process_issue_triage") @patch("main.send_comment_action")