fix(api): 修復 Incident-Approval 狀態同步 BUG
🔴 P0 核心功能修復: 問題: 審核後頁面重整,Y/n 按鈕重複出現 根因: resolve_incident_after_approval 在 Redis 缺失時靜默跳過 修復: 1. proposal_service.py - 處理 Redis 缺失情況 2. approvals.py - 添加詳細日誌追蹤 3. 設定 resolved_at 時間戳 防禦性增強: - 日誌記錄 metadata 內容 - 記錄 resolve 成功/失敗狀態 - 警告無 incident_id 的情況 長期規範: - 新增 feedback_incident_approval_sync.md 記憶 - 更新 HARD_RULES.md API 路徑規範 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,7 @@
|
||||
| API 整合 | `feedback_api_response_verification.md` |
|
||||
| 構建部署 | `feedback_build_from_git_only.md` |
|
||||
| **測試** | `feedback_no_mock_testing.md` 🔴🔴 禁止 Mock |
|
||||
| **API 路徑** | `feedback_api_path_naming.md` 🔴 修改需同步前端 |
|
||||
|
||||
## Skills 載入
|
||||
|
||||
|
||||
@@ -631,10 +631,19 @@ async def sign_approval(
|
||||
background_tasks.add_task(execute_approved_action, approval)
|
||||
|
||||
# Phase 6.5: 更新關聯的 Incident 狀態為 RESOLVED
|
||||
# 🔴 關鍵: 這是審核後 Incident 狀態更新的核心邏輯
|
||||
incident_id = approval.metadata.get("incident_id") if approval.metadata else None
|
||||
logger.info(
|
||||
"sign_approval_checking_incident",
|
||||
approval_id=str(approval_id),
|
||||
has_metadata=approval.metadata is not None,
|
||||
incident_id=incident_id,
|
||||
metadata_keys=list(approval.metadata.keys()) if approval.metadata else [],
|
||||
)
|
||||
|
||||
if incident_id:
|
||||
proposal_svc = get_proposal_service()
|
||||
await proposal_svc.resolve_incident_after_approval(
|
||||
resolve_success = await proposal_svc.resolve_incident_after_approval(
|
||||
incident_id=incident_id,
|
||||
approval_id=str(approval_id),
|
||||
)
|
||||
@@ -642,6 +651,20 @@ async def sign_approval(
|
||||
"incident_resolved_after_sign",
|
||||
incident_id=incident_id,
|
||||
approval_id=str(approval_id),
|
||||
success=resolve_success,
|
||||
)
|
||||
if not resolve_success:
|
||||
logger.error(
|
||||
"incident_resolve_failed",
|
||||
incident_id=incident_id,
|
||||
approval_id=str(approval_id),
|
||||
note="Incident status may not be updated correctly",
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
"sign_approval_no_incident_id",
|
||||
approval_id=str(approval_id),
|
||||
note="Approval has no incident_id in metadata, cannot update Incident status",
|
||||
)
|
||||
|
||||
return SignResponse(
|
||||
|
||||
@@ -553,6 +553,7 @@ class ProposalService:
|
||||
incident = Incident.model_validate_json(data)
|
||||
old_status = incident.status.value
|
||||
incident.status = IncidentStatus.RESOLVED
|
||||
incident.resolved_at = datetime.now(UTC)
|
||||
incident.updated_at = datetime.now(UTC)
|
||||
if incident.decision:
|
||||
incident.decision.state = "completed"
|
||||
@@ -564,6 +565,15 @@ class ProposalService:
|
||||
old_status=old_status,
|
||||
new_status="resolved",
|
||||
)
|
||||
else:
|
||||
# 🔴 關鍵修復: Redis 沒有 Incident 時,從 DB 讀取並更新
|
||||
logger.warning(
|
||||
"resolve_incident_redis_miss",
|
||||
incident_id=incident_id,
|
||||
note="Incident not found in Redis, will update DB only",
|
||||
)
|
||||
# 仍然標記為成功,讓 DB 更新繼續執行
|
||||
redis_ok = True
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
"resolve_incident_redis_error",
|
||||
@@ -572,6 +582,7 @@ class ProposalService:
|
||||
)
|
||||
|
||||
# 2. 更新 DB (如果存在)
|
||||
now = datetime.now(UTC)
|
||||
try:
|
||||
async with get_db_context() as db:
|
||||
stmt = select(IncidentRecord).where(
|
||||
@@ -581,12 +592,15 @@ class ProposalService:
|
||||
record = result.scalar_one_or_none()
|
||||
if record:
|
||||
record.status = "resolved"
|
||||
record.updated_at = datetime.now(UTC)
|
||||
record.resolved_at = now
|
||||
record.updated_at = now
|
||||
# 🔴 關鍵: 確保 commit 成功
|
||||
await db.commit()
|
||||
db_ok = True
|
||||
logger.info(
|
||||
"resolve_incident_db_updated",
|
||||
incident_id=incident_id,
|
||||
resolved_at=now.isoformat(),
|
||||
)
|
||||
else:
|
||||
# DB 沒有記錄但 Redis 有 - 這是可接受的狀態
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
| 架構 | 刪除 OpenClaw | OpenClaw 是核心 | [→ OpenClaw](#openclaw) |
|
||||
| Git | `--force` | 正常 push | [→ Git Safety](#git-safety) |
|
||||
| **測試** | **Mock 測試** | **真實 DB/服務** | [→ No Mock Testing](#no-mock-testing) |
|
||||
| **API** | **單獨改路徑** | **前後端同步** | [→ API Path Naming](#api-path-naming) |
|
||||
|
||||
---
|
||||
|
||||
@@ -147,6 +148,25 @@ git revert
|
||||
|
||||
---
|
||||
|
||||
## API Path Naming
|
||||
|
||||
**Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_api_path_naming.md`
|
||||
|
||||
```python
|
||||
# ❌ 禁止 - 單獨修改後端路徑
|
||||
@router.get("/ai-performance") # 改成 /incidents/ai-performance
|
||||
# 但前端仍調用 /ai-performance → 404
|
||||
|
||||
# ✅ 正確 - 前後端同步修改
|
||||
# 1. 後端: @router.get("/incidents/ai-performance")
|
||||
# 2. 前端: await fetch('/api/v1/stats/incidents/ai-performance')
|
||||
# 3. 測試: curl 驗證
|
||||
```
|
||||
|
||||
**原因:** 路徑變更是破壞性變更,必須同時更新前後端。
|
||||
|
||||
---
|
||||
|
||||
## No Mock Testing
|
||||
|
||||
**Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_no_mock_testing.md`
|
||||
|
||||
Reference in New Issue
Block a user