# ADR-047: Phase R 架構大掃除完整審查

| 項目 | 內容 |
|------|------|
| **狀態** | ✅ 審查通過 (97/100 OUTSTANDING) |
| **日期** | 2026-04-01 |
| **審查者** | 首席架構師 |
| **涵蓋範圍** | Phase R R1-R4 全部 + ADR-046 IncidentConverter |

---

## 審查總覽

| Phase | 任務 | 評分 | 狀態 |
|-------|------|------|------|
| R1 | 絞殺者包裝 (USE_NEW_ENGINE) | 25/25 | ✅ |
| R2 | 移除內嵌重複邏輯 | 24/25 | ✅ |
| R3 | Repository 層抽取 | 25/25 | ✅ |
| R4 | Router 層瘦身 | 23/25 | ✅ |
| ADR-046 | IncidentConverter 型別統一 | --- | ✅ 加分 |
| **合計** | | **97/100** | **OUTSTANDING** |

---

## R1: 絞殺者包裝 ✅ 25/25

### 設計意圖
以 `USE_NEW_ENGINE` 旗標為開關，保護遷移期間的服務可用性。
Strangler Fig Pattern Phase 1 (Identify + Wrap)。

### 驗證結果

| 檢查項 | 結果 |
|--------|------|
| `USE_NEW_ENGINE=True` 預設值 | ✅ 確認 |
| 新舊路徑可切換 | ✅ 架構完整 |
| 不影響現有行為 | ✅ 漸進遷移 |
| Phase 16 R1 commit 完成 | ✅ 歷史確認 |

> **P2-03 補充**: `USE_NEW_ENGINE` 旗標於 Phase R-R2 後已名存實亡（移除舊路徑後無法回滾至舊實作）。已正式標記失效，回滾方式改為 `git revert c7b3f8f d17b67c`。處置正確。

---

## R2: 移除內嵌重複邏輯 ✅ 24/25

### 刪除量

| 檔案 | 刪除行數 | 說明 |
|------|---------|------|
| `incident_memory.py` | -480 行 | DualIncidentMemory + IIncidentMemory 內嵌版 |
| `incident_engine.py` | -490 行 | IncidentEngine 內嵌版 + Lua 死碼 |
| **合計** | **-970 行** | |

### P0+P1 修復確認 (commit `d17b67c`)

| 優先級 | 項目 | 狀態 |
|--------|------|------|
| P0 | Redis key prefix 修正 (`awoooi:incidents:`) | ✅ |
| P0 | `_record_to_incident` 型別標注修正 (`Any`) | ✅ |
| P0 | 死碼 `LUA_SCRIPT` 移除 | ✅ |
| P1 | `IIncidentEngine.update_status` 簽名對齊 brain | ✅ |

### P2 技術債追蹤 (ADR-046)

| # | 問題 | 狀態 |
|---|------|------|
| P2-01 | `signal_worker` `persisted_to_pg` AttributeError | ✅ `getattr` 防禦 |
| P2-02 | `IIncidentEngine` Protocol 簽名不符 brain | ✅ `update_status` 對齊 |
| P2-03 | `USE_NEW_ENGINE` 旗標名存實亡 | ✅ 標記失效 + 回滾路徑更新 |

**扣分說明**: 文件同步略有延遲 (-1)，ADR-024 執行進度表已更新。

---

## R3: Repository 層抽取 ✅ 25/25

### 9 個 Repository 建立完成

| Repository | 職責 | 狀態 |
|-----------|------|------|
| `approval_repository.py` | 簽核記錄 CRUD | ✅ |
| `audit_log_repository.py` | 稽核日誌 | ✅ |
| `embedding_repository.py` | 向量嵌入 | ✅ |
| `incident_repository.py` | 事件記錄 | ✅ |
| `interfaces.py` | Repository Interface | ✅ |
| `k8s_repository.py` | K8s 狀態查詢 | ✅ |
| `learning_repository.py` | 學習記錄 | ✅ |
| `metrics_repository.py` | 指標數據 | ✅ |
| `playbook_repository.py` | 劇本查詢 | ✅ |

### 架構符合性

| ADR-024 標準 | 結果 |
|-------------|------|
| Repository 只含 SQL/ORM + Redis 操作 | ✅ |
| Service 層透過 Repository 存取資料 | ✅ |
| Router 層不直接呼叫 Repository | ✅ (Phase 22 P1 已清除) |

**Phase 22 首席架構師審查已通過，得分確認。**

---

## R4: Router 層瘦身 ✅ 23/25

### 四項任務完成狀態

| # | 任務 | 位置 | 狀態 | 修復 Phase |
|---|------|------|------|-----------|
| 127 | incidents.py Redis 直接操作 | `api/v1/incidents.py` | ✅ | Phase 17 P0 |
| 128 | approvals.py Lua Script | `api/v1/approvals.py` | ✅ | Phase 17 P0 |
| 129 | webhooks.py 業務邏輯抽取 | `api/v1/webhooks.py` | ✅ | 本 Session (`4118808`) |
| 130 | telegram.py 格式化 | `api/v1/telegram.py` | ✅ | Phase 22 P2 |

### #129 遷移成果詳細 (commit `4118808`)

| 遷移內容 | 舊位置 | 新位置 |
|---------|--------|--------|
| `AlertAnalyzer` class (141 行) | `webhooks.py:625-765` | `services/alert_analyzer_service.py` |
| `AlertPayload` model | `webhooks.py` | `models/webhook.py` |
| `AlertResponse` model | `webhooks.py` | `models/webhook.py` |
| `normalize_resource_name` 使用 | `webhooks.py` (直接呼叫) | `alert_analyzer_service.py` (ADR-016 整合) |
| **net 減少** | **-243 行** | |

### 現況驗證

```python
# webhooks.py 現在的 import 模式 ✅
from src.models.webhook import AlertPayload, AlertResponse          # models 層
from src.services.alert_analyzer_service import AlertAnalyzer       # services 層
from src.services.incident_service import get_incident_service      # Phase 17 P0
```

### 殘留項目說明

`generate_alert_fingerprint()` (22 行) 仍在 `webhooks.py:344`。

**分析**:
- 純函數，僅使用 `hashlib.sha256()` 對字串做 Hash
- 無 Redis/DB/外部 API 存取（符合 ADR-024 的核心禁令）
- 技術上為「業務邏輯 >10 行」，屬 P3 可接受殘留
- 建議未來 Phase 中移至 `services/alert_analyzer_service.py`

**扣分**: -2（殘留的 >10 行業務邏輯函數；但不含外部存取，風險極低）

---

## ADR-046: IncidentConverter 型別統一（加分項）

### 實作完成度

| 項目 | 檔案 | 狀態 |
|------|------|------|
| `brain_to_local()` | `utils/incident_converter.py` | ✅ |
| `local_to_brain()` | `utils/incident_converter.py` | ✅ |
| `IncidentEngineAdapter` | `services/incident_engine.py` | ✅ |
| `proposal_service` 清理 (#123) | `services/proposal_service.py` | ✅ commit `44840f5` |

### 邊界轉換點確認

| 位置 | 方向 | 狀態 |
|------|------|------|
| `IncidentDbAdapter._record_to_incident()` | DB→BrainIncident (brain 內部) | ✅ 邊界清晰 |
| `IncidentEngineAdapter.process_signal()` | BrainIncident→LocalIncident | ✅ |
| `IncidentEngineAdapter.get_incident()` | BrainIncident→LocalIncident | ✅ |
| `proposal_service._persist_incident()` | LocalIncident→BrainIncident (save) | ✅ |

### proposal_service #123 清理確認

```python
# 修復前 ❌
from src.core.redis_client import get_redis  # Router-like 直接 Redis
INCIDENT_KEY_PREFIX = "incident:"             # 錯誤 key prefix

# 修復後 ✅ (commit 44840f5)
async def _load_incident(self, incident_id: str) -> Incident | None:
    return await get_incident_engine().get_incident(incident_id)  # ADR-046

async def _persist_incident(self, incident: Incident) -> None:
    brain_incident = local_to_brain(incident)
    await get_incident_memory().save_incident(brain_incident)     # 正確 prefix
```

---

## 整體架構符合性

### ADR-024 四層架構最終狀態

```
┌─────────────────────────────────────────────────┐
│  Router Layer (api/v1/*.py)                     │
│  ✅ 無 Redis/DB 直接存取                         │
│  ✅ 無 AlertAnalyzer 等業務邏輯                  │
│  ⚠️ generate_alert_fingerprint() 純函數 (P3)    │
└─────────────────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────┐
│  Service Layer (services/*.py)                  │
│  ✅ AlertAnalyzer 正確位置                       │
│  ✅ IncidentEngineAdapter 轉換邊界               │
│  ✅ ProposalService 委派 IncidentEngine          │
└─────────────────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────┐
│  Repository Layer (repositories/*.py)           │
│  ✅ 9 個 Repository 全部建立                     │
│  ✅ 介面分離 (interfaces.py)                     │
└─────────────────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────┐
│  Model Layer (models/*.py)                      │
│  ✅ AlertPayload/AlertResponse 正確位置          │
│  ✅ IncidentConverter 邊界明確                   │
└─────────────────────────────────────────────────┘
```

---

## P2 技術債清單（Phase S 追蹤）

| # | 項目 | 位置 | 優先級 |
|---|------|------|--------|
| S-01 | `generate_alert_fingerprint()` 移至 Service | `webhooks.py:344` | P3 |
| S-02 | `USE_NEW_ENGINE` config 項清除 | `core/config.py` | P3 |

---

## 審查結論

**評分: 97/100 OUTSTANDING** ✅

Phase R 絞殺者模式四階段全部完成。核心指標：
- **-970 行** 重複邏輯移除（最大技術債清除）
- **9 個 Repository** 建立，分層架構完整
- **AlertAnalyzer** (141 行業務邏輯) 從 Router 遷出
- **ADR-046 IncidentConverter** 型別邊界清晰

遺留 2 分：`generate_alert_fingerprint()` 純函數尚在 Router 文件，
為 P3 低風險殘留，不影響系統正確性。

> **首席架構師簽核**: Phase R 完整通過，可進入下一個 Phase。
> **簽核日期**: 2026-04-01 (台北時間)
