fix(approval): NO_ACTION 不再誤標 EXECUTION_FAILED (MASTER §7.1 #11 修)
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:
OG T
2026-04-19 01:08:16 +08:00
parent 2e988bdb81
commit fdce0a3ab9

View File

@@ -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",