diff --git a/apps/api/src/services/approval_execution.py b/apps/api/src/services/approval_execution.py index b8d1c5de..3de07695 100644 --- a/apps/api/src/services/approval_execution.py +++ b/apps/api/src/services/approval_execution.py @@ -144,14 +144,57 @@ class ApprovalExecutionService: namespace = parsed.namespace if operation_type is None or resource_name is None: + # 2026-04-19 ogt + Claude Opus 4.7: 區分 NO_ACTION vs 真解析失敗 + # NO_ACTION 是 AI 刻意選的「純調查不破壞」,不該誤標 EXECUTION_FAILED + # 污染 auto_execute 成功率 KPI (MASTER §7.1 #11) + _action_upper = (approval.action or "").upper() + _is_no_action = ( + "NO_ACTION" in _action_upper + or "NO-ACTION" in _action_upper + or "NOACTION" in _action_upper + or "(未設)" in approval.action + or _action_upper.startswith("OBSERVE") + or _action_upper.startswith("INVESTIGATE") + ) + + if _is_no_action: + logger.info( + "background_execution_noop", + approval_id=str(approval.id), + action=approval.action, + reason="NO_ACTION - 純調查/觀察類,不執行破壞動作", + ) + # 標為 SUCCESS (觀察/調查本身就是成功完成) + await service.update_execution_status(approval.id, success=True) + await timeline.add_event( + event_type="exec", + status="success", + title="✅ 純觀察類動作完成 (NO_ACTION)", + description=f"Action: {approval.action[:120]}", + actor="leWOOOgo", + actor_role="executor", + approval_id=str(approval.id), + ) + # 執行結果 reply 原告警卡片 + asyncio.create_task( + self._push_execution_result_to_alert( + approval, success=True, error=None, + ) + ) + return True # NO_ACTION 視為成功完成 + + # 真解析失敗 (非 NO_ACTION) logger.warning( "background_execution_skip", approval_id=str(approval.id), reason="Could not parse operation type from action", action=approval.action, ) - # Phase 5: 更新資料庫狀態 - await service.update_execution_status(approval.id, success=False) + # Phase 5: 更新資料庫狀態 + 帶 error_message (P0.2) + await service.update_execution_status( + approval.id, success=False, + error_message=f"Could not parse operation type from action: {approval.action[:150]}", + ) await timeline.add_event( event_type="exec", status="error",