feat(adr100): bridge playbook authoring approvals
All checks were successful
CD Pipeline / tests (push) Successful in 1m20s
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / build-and-deploy (push) Successful in 7m44s
CD Pipeline / post-deploy-checks (push) Successful in 2m49s

This commit is contained in:
Your Name
2026-06-01 20:48:24 +08:00
parent f0daaccbba
commit 16775bb4fa
9 changed files with 784 additions and 7 deletions

View File

@@ -72,6 +72,15 @@ _SSH_GATEWAY_TOOL_SCOPES: dict[str, str] = {
}
def _is_playbook_authoring_record_only_approval(approval: ApprovalRequest) -> bool:
metadata = dict(getattr(approval, "metadata", None) or {})
return (
metadata.get("approval_kind") == "adr100_playbook_authoring"
and metadata.get("execution_kind") == "playbook_authoring_record_only"
and metadata.get("execution_authorized") is False
)
class ApprovalExecutionService:
"""
授權執行服務 - 編排整個執行流程
@@ -254,6 +263,70 @@ class ApprovalExecutionService:
pass
if operation_type is None or resource_name is None:
if _is_playbook_authoring_record_only_approval(approval):
metadata = dict(getattr(approval, "metadata", None) or {})
logger.info(
"background_execution_playbook_authoring_record_only",
approval_id=str(approval.id),
action=approval.action,
incident_id=getattr(approval, "incident_id", None),
work_item_id=metadata.get("work_item_id"),
playbook_id=metadata.get("playbook_id"),
)
await service.update_execution_status(
approval.id,
success=True,
execution_kind="playbook_authoring_record_only",
repair_executed=False,
repair_attempted=False,
)
await timeline.add_event(
event_type="exec",
status="success",
title="PlayBook authoring approval recorded (no runtime repair)",
description=(
"Approval scope is record-only. No incident state change, "
"no runtime repair, no verified_success promotion."
),
actor="leWOOOgo",
actor_role="executor",
approval_id=str(approval.id),
incident_id=approval.incident_id,
)
await self._log_aol_completed(
op_id=_aol_op_id,
status="success",
duration_ms=int((time.time() - _aol_started_ms) * 1000),
output={
"reason": "PLAYBOOK_AUTHORING_RECORD_ONLY",
"execution_kind": "playbook_authoring_record_only",
"repair_executed": False,
"repair_attempted": False,
"work_item_id": metadata.get("work_item_id"),
"playbook_id": metadata.get("playbook_id"),
},
)
await self._log_alert_execution_completed(
approval,
success=True,
execution_kind="playbook_authoring_record_only",
duration_ms=int((time.time() - _aol_started_ms) * 1000),
output={
"reason": "PLAYBOOK_AUTHORING_RECORD_ONLY",
"repair_executed": False,
"repair_attempted": False,
},
)
await self._push_execution_result_to_alert(
approval,
success=True,
error=None,
execution_kind="playbook_authoring_record_only",
repair_executed=False,
repair_attempted=False,
)
return True
# 2026-04-19 ogt + Claude Opus 4.7: 區分 NO_ACTION vs 真解析失敗
# NO_ACTION 是 AI 刻意選的「純調查不破壞」,不該誤標 EXECUTION_FAILED
# 污染 auto_execute 成功率 KPI (MASTER §7.1 #11)