diff --git a/apps/api/src/services/approval_execution.py b/apps/api/src/services/approval_execution.py
index 3de07695..871469b0 100644
--- a/apps/api/src/services/approval_execution.py
+++ b/apps/api/src/services/approval_execution.py
@@ -496,11 +496,53 @@ class ApprovalExecutionService:
settings = get_settings()
gateway = get_telegram_gateway()
+ # 2026-04-19 ogt + Claude Opus 4.7 修 AP-2: 除了 reply 外,
+ # 也 edit 原卡片移除按鈕 + 更新狀態戳記(避免卡片永遠停在「執行中」)
+ try:
+ await gateway._send_request("editMessageReplyMarkup", {
+ "chat_id": settings.OPENCLAW_TG_CHAT_ID,
+ "message_id": orig_msg_id,
+ "reply_markup": {"inline_keyboard": []},
+ })
+ except Exception as _edit_e:
+ logger.debug("push_execution_edit_buttons_failed",
+ approval_id=str(approval.id), error=str(_edit_e))
+
+ # 附加 KM/Playbook 增量(查最近該 incident 的 KM + playbook 使用)
+ km_info = ""
+ try:
+ from sqlalchemy import text as _sql
+ from src.db.base import get_db_context
+ async with get_db_context() as _db:
+ _km_row = await _db.execute(
+ _sql("""SELECT COUNT(*) FROM knowledge_entries
+ WHERE created_at > NOW() - interval '2 minutes'"""),
+ )
+ _km_count = _km_row.scalar() or 0
+ _pb_row = await _db.execute(
+ _sql("""SELECT COUNT(*) FROM playbooks
+ WHERE updated_at > NOW() - interval '2 minutes'"""),
+ )
+ _pb_count = _pb_row.scalar() or 0
+ if _km_count or _pb_count:
+ km_info = f"\n📚 KM +{_km_count} 🎯 Playbook 更新×{_pb_count}"
+ except Exception:
+ pass
+
if success:
- text = f"✅ 執行成功\n{(approval.action or '')[:180]}"
+ text = (
+ f"✅ 執行成功\n"
+ f"{(approval.action or '')[:180]}"
+ f"{km_info}"
+ )
else:
err_short = (error or "未知錯誤")[:150]
- text = f"❌ 執行失敗\n{(approval.action or '')[:180]}\n原因: {err_short}"
+ text = (
+ f"❌ 執行失敗\n"
+ f"{(approval.action or '')[:180]}\n"
+ f"原因: {err_short}"
+ f"{km_info}"
+ )
await gateway._http_client.post(
f"https://api.telegram.org/bot{settings.OPENCLAW_TG_BOT_TOKEN}/sendMessage",
diff --git a/apps/api/src/services/openclaw.py b/apps/api/src/services/openclaw.py
index 6495452d..c81730a8 100644
--- a/apps/api/src/services/openclaw.py
+++ b/apps/api/src/services/openclaw.py
@@ -1269,7 +1269,12 @@ class OpenClawService:
data["confidence"] = 0.0 # 截斷/缺失 → 0.0,不可偽造
if "risk_level" not in data:
data["risk_level"] = "low"
- if "primary_responsibility" not in data:
+ # 2026-04-19 ogt + Claude Opus 4.7 修 AP-3:
+ # primary_responsibility 有時 LLM 填空字串/None → resp_display 顯示「❓ 未知」
+ # 強制正規化: 空/None/不在白名單 → 用 kubectl 有無推 INFRA 或 BE (非「未知」)
+ _valid_resp = {"FE", "BE", "INFRA", "DB", "COLLAB"}
+ _cur_resp = str(data.get("primary_responsibility") or "").strip().upper()
+ if _cur_resp not in _valid_resp:
data["primary_responsibility"] = "INFRA" if "kubectl" in str(data) else "BE"
if "suggested_action" not in data:
data["suggested_action"] = "RESTART_DEPLOYMENT" if "restart" in str(data).lower() else "NO_ACTION"
diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py
index 26bed6fa..5aafdd0f 100644
--- a/apps/api/src/services/telegram_gateway.py
+++ b/apps/api/src/services/telegram_gateway.py
@@ -1717,6 +1717,35 @@ class TelegramGateway:
except Exception as _db_e:
logger.warning("notification_outcomes_db_write_failed", error=str(_db_e))
+ # 2026-04-19 ogt + Claude Opus 4.7: 修 AP-1 — message_id 同時存進
+ # approval_records.telegram_message_id,不只 Redis(重啟會丟)
+ if _msg_id:
+ try:
+ from src.services.approval_db import get_approval_service
+ _svc = get_approval_service()
+ if hasattr(_svc, "update_telegram_message"):
+ # 若有 update_telegram_message 方法(通常用 incident_id)
+ # 先用 incident_id 更新,再 fallback 直接 UPDATE approval_records
+ from sqlalchemy import text as _sql2
+ from src.db.base import get_db_context as _gdc
+ async with _gdc() as _db2:
+ await _db2.execute(
+ _sql2("""
+ UPDATE approval_records
+ SET telegram_message_id = :mid,
+ telegram_chat_id = :cid
+ WHERE id = :aid
+ """),
+ {
+ "mid": int(_msg_id),
+ "cid": int(settings.OPENCLAW_TG_CHAT_ID),
+ "aid": str(approval_id),
+ },
+ )
+ except Exception as _db_e2:
+ logger.warning("approval_tg_msg_id_db_persist_failed",
+ approval_id=str(approval_id), error=str(_db_e2))
+
# 2026-04-10 Claude Sonnet 4.6 Asia/Taipei: 儲存 message_id 供自動修復後更新卡片
# key: tg_approval:{approval_id},TTL 24h
if _msg_id: