# INV-9: Global Singleton Catalog

**版本**：v1.0 初稿
**日期**：2026-05-03（台北）
**來源**：`apps/api/src/` grep `_instance = None` / `get_*()` factory functions
**用途**：ADR-124 Global Singleton Decomposition 的執行基礎

---

## 概覽

| 統計 | 數量 |
|------|------|
| 確認全域單例 | 13 |
| 需要分解（per-project）| 9 |
| 可保持 platform-level | 4 |

---

## 完整清單

### 1. TrustEngine

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/core/trust_engine.py:189` |
| Factory | `get_trust_engine()` at line 393 |
| 單例模式 | module-level `_trust_engine` variable |
| 目前狀態 | 全域，所有 tenant 共用 |
| 影響 | trust_records（無 project_id），trust score 跨 tenant 混算 |
| 分解策略 | per-project trust engine instance，從 project contract 讀 trust_policy |
| Phase | Phase 2（repository 加 project_id filter 後）|
| 風險 | 高（TrustEngine 是飛輪核心，分解需仔細 regression test）|

---

### 2. ProviderRegistry（MCP）

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/plugins/mcp/registry.py:74` |
| Factory | module-level `get_registry()` at line 80 / `_registry` at line 209 |
| 目前狀態 | 全域，所有 tenant 共用 MCP provider 清單 |
| 影響 | Phase 5 MCP Gateway：不同 tenant 應有不同可用 tool 清單 |
| 分解策略 | platform-level registry（tool 清單）+ per-project grants（誰可用什麼）；Registry 本身可保持 platform-level |
| Phase | Phase 5（MCP Gateway 建立時）|
| 風險 | 中（Registry 改為 read-only platform resource，grants 移到 DB）|

---

### 3. AnomalyCounter

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/anomaly_counter.py:85` |
| Factory | `_anomaly_counter = AnomalyCounter(get_redis())` at line 800 |
| 目前狀態 | 全域，6 個 Redis prefix 無 tenant 隔離 |
| 影響 | P1-17：多 tenant 計數混算 |
| 分解策略 | per-project 工廠：`get_anomaly_counter(project_id)` 注入 `project_prefix` |
| Phase | Phase 2（PR-11）|
| 風險 | 中（6 個 prefix 全部要改）|

---

### 4. AIRouter

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/ai_router.py:194` |
| Factory | `_router = AIRouter()` at line 1324 |
| 依賴 | AIProviderRegistry（923）、AIRouterExecutor（975）|
| 目前狀態 | 全域，所有 tenant 共用 provider routing 決策 |
| 影響 | EwoooC 可能使用 AWOOOI 的 model routing policy |
| 分解策略 | AIRouter 接受 EffectivePolicy 參數（per-project policy 已在 Phase 4 設計）；Router 邏輯保持 platform-level，policy 來源改為 per-project |
| Phase | Phase 4（EffectivePolicy 設計完成後）|
| 風險 | 中（ADR-052 AIRouter 已有 protocol 設計，改 policy 注入點）|

---

### 5. IntentClassifier

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/intent_classifier.py:357` |
| Factory | `_classifier = IntentClassifier()` at line 744 |
| 目前狀態 | 全域 |
| 影響 | 不同 tenant 可能需要不同 intent classification domain |
| 分解策略 | 短期保持 platform-level（intent schema 共用）；長期 per-project intent domain config |
| Phase | Phase 6（EwoooC 需要不同 intent domain 時才分解）|
| 風險 | 低（intent classification 不帶 tenant 資料）|

---

### 6. TelegramGateway

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/telegram_gateway.py:1324` |
| Factory | `_gateway = TelegramGateway()` at line 6375 |
| 目前狀態 | 全域，持有 polling leader lock |
| 影響 | `telegram:polling:leader` 是 platform_resource，但 gateway 本身可以 per-project |
| 分解策略 | polling leader 保持 platform_resource；gateway 處理邏輯改為 per-project（不同 tenant 有不同 Telegram bot）|
| Phase | Phase 7（Communication Hub 建立時）|
| 風險 | 高（TelegramGateway 是最大的 service，約 6000+ 行）|

---

### 7. DecisionManager

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/decision_manager.py:1402` |
| Factory | `_decision_manager = DecisionManager()` at line 3529 |
| 目前狀態 | 全域，硬碼 silence key（P1-24 已在 PR-02 修補）|
| 影響 | 決策邏輯跨 tenant 共用（不同 tenant 決策規則不同）|
| 分解策略 | DecisionManager 接受 project_id 參數；規則從 project policy contract 讀 |
| Phase | Phase 4（EffectivePolicy 設計完成後）|
| 風險 | 極高（DecisionManager 是 Tier 3 核心，修改需 ADR 和架構審查）|

---

### 8. ConsensusEngine

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/consensus_engine.py:344` |
| Factory | `_consensus_engine = ConsensusEngine()` at line 716 |
| 目前狀態 | 全域，`CONSENSUS_PREFIX="consensus:"` 無 project |
| 影響 | P0-12：多 tenant consensus 結果混合 |
| 分解策略 | ConsensusEngine 接受 project_id；CONSENSUS_PREFIX 改為 `{project_id}:consensus:` |
| Phase | Phase 2（PR-06）|
| 風險 | 中（需 Redis 雙寫 Phase A 先完成）|

---

### 9. DecisionFusionAdapter

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/decision_fusion_adapter.py:538` |
| 單例模式 | `_adapter_instance = None` |
| 目前狀態 | 全域 |
| 影響 | 決策融合結果跨 tenant |
| 分解策略 | 同 DecisionManager（依賴它），Phase 4 一起改 |
| Phase | Phase 4 |
| 風險 | 中 |

---

### 10. FailoverAlerter

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/failover_alerter.py:395` |
| 單例模式 | `_alerter_instance = None` |
| 目前狀態 | 全域 |
| 影響 | failover 告警沒有 tenant 隔離（GCP Ollama failover 是 platform_resource）|
| 分解策略 | failover 告警本身是 `platform_resource`（保持全域），但告警發送目的地改為 per-project channel |
| Phase | Phase 7（Communication Hub 建立後）|
| 風險 | 低 |

---

### 11. HostRepairAgent

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/host_repair_agent.py:204` |
| 單例模式 | `cls._instance = None` |
| 目前狀態 | 全域 |
| 影響 | 主機修復操作無 tenant scope（主機屬於 platform）|
| 分解策略 | 保持 `platform_internal`（主機修復是 platform 操作，不是 tenant 操作）|
| Phase | N/A（不需分解）|
| 風險 | 低 |

---

### 12. AIProviderRegistry

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/ai_router.py:923` |
| 目前狀態 | AIRouter 的內部 registry，全域 |
| 影響 | provider 清單是 platform resource（GCP Ollama/Gemini/etc 所有 tenant 共用）|
| 分解策略 | 保持 platform-level registry；per-project policy 控制各 tenant 的可用 provider |
| Phase | Phase 4（EffectivePolicy 設計）|
| 風險 | 低 |

---

### 13. AIRouterExecutor

| 屬性 | 值 |
|------|----|
| 位置 | `apps/api/src/services/ai_router.py:975` |
| 目前狀態 | AIRouter 的執行器，全域 |
| 影響 | 執行邏輯共用，但 policy 會 per-project |
| 分解策略 | 同 AIRouter（Phase 4）|
| Phase | Phase 4 |
| 風險 | 中 |

---

## 分解優先序（ADR-124 執行計畫）

| Phase | 要分解的單例 | 策略 |
|-------|-----------|------|
| Phase 2 | AnomalyCounter（PR-11）、ConsensusEngine（PR-06）| 注入 project_id 參數 |
| Phase 4 | AIRouter、AIProviderRegistry、AIRouterExecutor、DecisionFusionAdapter | EffectivePolicy 注入 |
| Phase 5 | ProviderRegistry | per-project grants 移到 DB，registry 保持 platform-level |
| Phase 6 | IntentClassifier | EwoooC 需要不同 intent domain 時 |
| Phase 7 | TelegramGateway、FailoverAlerter | Communication Hub 建立 |
| **特別謹慎** | **TrustEngine**、**DecisionManager** | Tier 3，需額外 ADR 和架構審查 |

> **TrustEngine 和 DecisionManager 分解需要統帥批准（Tier 3 核心，RED_ZONES.md）**

---

## 不需分解的單例（platform_resource）

| 單例 | 理由 |
|------|------|
| HostRepairAgent | 主機修復 = platform 操作，不屬於任何 tenant |
| AIProviderRegistry | provider 清單 = platform resource |
| TelegramGateway 的 polling leader | platform_resource（鎖，全域唯一）|

---

## 驗收標準

- [ ] 13 個單例全部分類完成（per-project / platform-level / platform_resource）
- [ ] TrustEngine 和 DecisionManager 的分解計畫需獨立 ADR（Tier 3 審查）
- [ ] Phase 2 前：AnomalyCounter + ConsensusEngine 分解完成

*最後更新：2026-05-03（台北）*
