fix(approval): NO_ACTION 不再誤標 EXECUTION_FAILED (MASTER §7.1 #11 修)
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
2026-04-19 凌晨(台北時區)— ogt + Claude Opus 4.7 (1M) 根因: approval.action='NO_ACTION - 待分析' (幻覺 validator 降級產物) 丟進 parse_operation_from_action → operation_type=None → background_execution_skip → update_execution_status(success=False) → 標為 EXECUTION_FAILED。 污染 KPI: MASTER §7.1 #11 auto_execute 成功率 = EXECUTION_SUCCESS / (SUCCESS+FAILED) NO_ACTION 本來就不該計入失敗,但卻被算進去拖垮指標。 實測 30d 成功率 0.9% 有很大比例是 NO_ACTION 誤標造成。 修復: parse 失敗時先判斷是否 NO_ACTION 類 (action 含 NO_ACTION/OBSERVE/INVESTIGATE 等關鍵字) → 走專屬 noop 分支: - log event=background_execution_noop (info 級) - update_execution_status(success=True) → EXECUTION_SUCCESS - timeline 標 ✅ 純觀察類動作完成 - reply 原告警卡片顯示成功 - return True 真正解析失敗 (非 NO_ACTION) 保留原失敗路徑,但補上 error_message (P0.2 延伸),讓 rejection_reason 有 "Could not parse operation type from action: <action>" 而非空字串。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user