# AWOOOI 絕對禁止規則 (Hard Rules) > 違反任何一條 = 重大事故 --- ## 文件資訊 | 欄位 | 值 | |------|-----| | **版本** | v2.1 | | **建立日期** | 2026-03-20 (台北) | | **建立者** | Claude Code | | **最後修改** | 2026-05-06 (台北) | | **修改者** | Codex + ogt (新增文件語言鐵律) | ### 變更紀錄 | 版本 | 日期 | 執行者 | 變更內容 | |------|------|--------|----------| | v1.0 | 2026-03-20 | Claude Code | 初始建立 | | v1.1 | 2026-03-23 | Claude Code | 新增 No Mock Testing | | v1.2 | 2026-03-24 | Claude Code | 新增 Deployment Verification | | v1.3 | 2026-03-25 | Claude Code | 新增 Timezone Taipei | | v1.4 | 2026-03-25 | Claude Code | 新增 Change Annotation + 文件資訊區塊 | | v1.5 | 2026-03-26 | Claude Code | 關聯紅區治理 (RED_ZONES.md) | | v1.6 | 2026-03-30 | Claude Code | 🔴🔴🔴 前端內網 IP 禁令 (瀏覽器權限事故) | | v1.7 | 2026-04-02 | Claude Code | Phase 24 AI Router 重構規範 (DI/隱私/絞殺者) | | v1.8 | 2026-04-03 | Claude Code | 🔴🔴🔴 費用變更強制審批 (統帥指示) | | v1.9 | 2026-04-15 | Claude Code + ogt | 🔴🔴🔴 AI 自主化飛輪 Phase 退出條件鐵律 (ADR-080) | | v2.0 | 2026-04-16 | Claude Code + ogt | 新增 No Island Coding / 主動執行熔斷機制 / 自循環工作流 / 狀態機驗證鐵律 | | v2.1 | 2026-05-06 | Codex + ogt | 🔴 文件語言鐵律:Markdown/ADR/LOGBOOK/Runbook/交接文件一律繁體中文 | --- ## 快速索引 | 主題 | 禁止 | 正確做法 | 詳細規則 | |------|------|---------|---------| | CI/CD | `ubuntu-latest` | `self-hosted` | [→ GitHub Billing](#github-billing) | | Telegram | `logOut()` | 先停後換 | [→ Telegram Token](#telegram-token) | | 前端 | 硬編碼文字 | `next-intl` | [→ i18n](#i18n) | | 文件 | 英文主文 | 繁體中文 | [→ 文件語言規範](#文件語言規範) | | 資料庫 | SQLite | PostgreSQL | [→ DB](#database) | | CORS | `*` | 白名單 | [→ CORS](#cors) | | 數據 | 假數據 Demo | 真實 API | [→ No Fake Data](#no-fake-data) | | 架構 | 刪除 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) | | **部署** | **假設已部署** | **驗證 Pod 版本** | [→ Deployment Verification](#deployment-verification) | | **Alertmanager** | **指向 OpenClaw** | **指向 AWOOOI API** | [→ Alertmanager Routing](#alertmanager-routing) | | **簽核 UI** | **清空內容** | **保留原始內容** | [→ Approval Preserve Content](#approval-preserve-content) | | **時區** | **UTC/utcnow** | **台北時區 +8** | [→ Timezone Taipei](#timezone-taipei) | | **變更追蹤** | **無註解** | **人事物+版本+台北時區** | [→ Change Annotation](#change-annotation) | | **🔴🔴🔴 前端建置** | **內網 IP** | **公網域名** | [→ Frontend Internal IP](#frontend-internal-ip) | | **AI Router** | **Router import 具體 Provider** | **只依賴 Protocol** | [→ OpenClaw](#openclaw) | | **🔴🔴🔴 費用變更** | **擅自切換/新增付費 AI Provider** | **先讀憲法第五章,再請統帥批准** | [→ Cost Change Approval](#cost-change-approval) | | **🔴🔴🔴 AI 飛輪 Phase** | **未過退出條件就宣告完成** | **必須逐條驗收 exit conditions** | [→ AI Phase Exit Conditions](#ai-phase-exit-conditions) | | **🔴🔴 孤島開發** | **局部最佳化不查 Callers** | **grep 全域掃描後才動手** | [→ No Island Coding](#no-island-coding) | | **🔴🔴 熔斷機制** | **不確定就停下來問** | **主動做完,爆炸半徑 >3 模組才熔斷** | [→ 主動執行與熔斷](#proactive-execution--circuit-breaker) | | **🔴🔴 工作流節奏** | **每步都來回報** | **內部自循環,全局單次回報** | [→ 自循環工作流](#self-loop-workflow) | | **🟡 狀態機驗證** | **不查中間狀態卡死** | **必驗 TTL + Cleanup + Fallback** | [→ State & Flow Validation](#state--flow-validation) | --- ## 🔴🔴🔴 Cost Change Approval — 費用變更強制審批 > **統帥指示 2026-04-03**: 所有涉及費用產生的變更,必須先看憲法規定,再經統帥批准。 ### 定義:什麼是「涉及費用的變更」 | 類型 | 範例 | 是否需審批 | |------|------|-----------| | 新增/切換付費 AI Provider | 改用 Claude API、新增 GPT-4 | ✅ **必須審批** | | 增加現有 Provider 呼叫頻率 | 告警觸發改為每分鐘 | ✅ **必須審批** | | 取消 feature flag 限制 | 關掉 token 上限 | ✅ **必須審批** | | 新增外部付費 API 整合 | Sentry、Langfuse 付費功能 | ✅ **必須審批** | | 修改現有 Provider timeout | 45s → 90s(延遲但不增費用) | ❌ 不需審批 | | 純粹程式邏輯優化 | Prompt 精簡、快取策略 | ❌ 不需審批 | ### 強制流程 ``` 發現需要涉及費用的變更 ↓ 1. 閱讀憲法第五章 (費用治理) ↓ 2. 評估: 月費用影響估算 (USD) ↓ 3. 停下來,向統帥說明: - 為什麼需要這個變更 - 預估費用影響 - 替代方案評估 ↓ 4. 等待統帥明確批准 ("好" / "同意" / "執行") ↓ 5. 執行變更,並在 commit 備註批准日期 ``` ### 🔴 絕對禁止 ``` ❌ 未獲批准,擅自切換到新的付費 AI Provider ❌ 未獲批准,增加現有 Provider 的呼叫頻率或 token 上限 ❌ 以「暫時測試」為由繞過審批流程 ❌ 在沒有費用評估的情況下上線新的 AI 功能 ``` ### 今日違規案例 (2026-04-03,教訓記錄) > Claude Code 未經批准將 ChatManager 的 OpenClaw 替換為 Gemini Flash, > 理由是「Ollama 卡死」。這是錯誤的 — 應先報告統帥,等待批准, > 再評估是否切換。費用影響未經評估,架構決策未經統帥核可。 --- ## 🔴🔴🔴 Frontend Internal IP **Memory:** `feedback_docker_nextjs_api_url.md` + `feedback_sentry_local_network.md` **絕對禁止** 在 CD 建置時使用內網 IP: ```yaml # ❌ 禁止 - 觸發瀏覽器「存取區域網路」權限對話框 --build-arg NEXT_PUBLIC_API_URL=http://192.168.0.125:32334 --build-arg NEXT_PUBLIC_SENTRY_DSN=http://...@192.168.0.110:9000/2 # ✅ 正確 - 使用公網域名 --build-arg NEXT_PUBLIC_API_URL=https://awoooi.wooo.work ``` **為什麼這是 Hard Rule:** - `NEXT_PUBLIC_*` 是 **build-time** 變數,會寫死到 JS Bundle - Runtime 設定 (K8s ConfigMap/Secret) 對 Next.js 無效 - 內網 IP 會觸發瀏覽器安全機制,彈出權限對話框 - UX 極差,尤其無痕模式 **2026-03-30 事故回顧:** CD Pipeline 使用 `http://192.168.0.125:32334` 建置,導致所有 API 請求指向內網 VIP。 --- ## GitHub Billing **Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_github_billing.md` ```yaml # ❌ 禁止 runs-on: ubuntu-latest # ✅ 正確 runs-on: self-hosted ``` **原因:** GitHub Actions 帳戶額度限制,必須用 192.168.0.110 的 self-hosted runner。 --- ## Telegram Token **Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_telegram_token_disaster.md` ```python # ❌ 禁止 - 會導致 Token 永久失效 await bot.log_out() # ✅ 正確流程 1. 先停止舊 Bot 的 Long Polling 2. 再切換新 Token ``` **原因:** 2026-03-23 災難事件,logOut 導致 Token 永久失效。 --- ## i18n **Memory:** - `feedback_i18n_zero_hardcode.md` - 零容忍硬編碼 - `feedback_i18n_language_strategy.md` - 語言選擇標準 ```tsx // ❌ 禁止硬編碼 STATE: IDLE // ✅ 使用 next-intl {t('agent.state')}: {t('agent.idle')} ``` | 元素 | 語言 | 範例 | |------|------|------| | UI 文字 | 繁體中文 | 「系統穩定」 | | 技術標識 | 英文 | `P0`, `RESOLVED` | **原因:** 面向使用者的文字必須繁體中文。 --- ## 文件語言規範 **統帥指示 2026-05-06**:所有文件必須使用繁體中文。 ### 適用範圍 | 類型 | 規範 | |------|------| | Markdown 文件 | 主文、標題、表格說明、結論一律繁體中文 | | ADR / LOGBOOK / Runbook | 一律繁體中文,避免英文段落混入 | | 交接文件 / implementation plan | 一律繁體中文,讓跨 session 接手時不再重譯 | | 前端使用者可見文字 | 仍遵守 i18n,不得硬編碼 | ### 可保留英文的內容 | 類型 | 範例 | |------|------| | 程式符號與路徑 | `project_id`, `apps/api/src`, `MCPToolResult` | | API / 指令 / 錯誤碼 | `GET /v1/platform/runs`, `kubectl`, `E-MCP-GATE-001` | | 服務與產品名稱 | `Alertmanager`, `Sentry`, `ClickHouse`, `Gemini` | | 原始 log / trace / commit message | 保留原文,必要時在下方補繁中解釋 | ### 絕對禁止 ``` ❌ 新增英文主文的 ADR、Runbook、LOGBOOK 或 handoff 文件 ❌ 將中文文件改成中英混雜但沒有必要技術原因 ❌ 用英文標題包裝中文專案狀態,造成後續 session 還要重譯 ``` --- ## Database **Memory:** AWOOOI 憲法 ```python # ❌ 禁止 DATABASE_URL = "sqlite:///..." # ✅ 正確 DATABASE_URL = "postgresql+asyncpg://..." ``` **原因:** SQLite 無法支援生產環境並發。 --- ## CORS **Memory:** AWOOOI 憲法 ```python # ❌ 禁止 CORS_ORIGINS = ["*"] # ✅ 正確 CORS_ORIGINS = ["https://awoooi.wooo.work", "http://localhost:3000"] ``` **原因:** 安全性要求。 --- ## No Fake Data **Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_no_fake_data.md` ```tsx // ❌ 禁止 const data = DEMO_DATA // ✅ 正確 const { data } = useRealAPI() ``` **原因:** 假數據導致用戶無法看到真實系統狀態。 --- ## OpenClaw **Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_architecture_openclaw_core.md` ``` ❌ 禁止: 淘汰、取代、或刪除 OpenClaw ✅ 正確: OpenClaw 是 AWOOOI 產品核心,只能增強不能移除 ``` **原因:** OpenClaw AI 是產品核心價值。 ### Phase 24 AI Router 重構規範 (ADR-052, 2026-04-02) ``` ❌ 禁止: 在 AIRouter (router.py) 中 import 具體 Provider 實作類別 ✅ 正確: Router 只依賴 AIProvider Protocol,Provider 在 main.py DI 註冊 ❌ 禁止: 將 ConsensusEngine (P0/P1) 納入 AIRouter ✅ 正確: ConsensusEngine 是獨立決策層,走 Claude Agent SDK ❌ 禁止: DIAGNOSE/CODE_REVIEW 意圖路由到 cloud Provider ✅ 正確: privacy_level="local" 強制本地 (零信任 ADR-023) ❌ 禁止: 遷移期間刪除 openclaw.py 舊 fallback chain ✅ 正確: USE_AI_ROUTER 絞殺者開關,新舊並存至 Phase B4 ``` **Memory:** `project_phase24_ai_router.md` --- ## Git Safety **Memory:** 防禦性工程 ```bash # ❌ 禁止 git push --force git reset --hard git checkout -- . # ✅ 正確 git push 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` ```python # ❌ 禁止 - 全面禁止 Mock 測試 from unittest.mock import Mock, AsyncMock, MagicMock, patch mock_service = AsyncMock() with patch("src.services.xxx", mock_service): ... # ✅ 正確 - 使用真實資料庫/服務 async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client: response = await client.post("/api/v1/xxx", json=payload) ``` **原因:** 統帥 2026-03-24 明確指示「全面禁止!!!」Mock 無法反映真實系統行為。 **允許例外:** - `patch.object(settings, ...)` 修改配置值(非 Mock 服務) --- ## Deployment Verification **Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_deployment_verification.md` ```bash # ❌ 禁止 - 假設 git push 就是部署完成 git push && echo "已部署" # ✅ 正確 - 必須驗證 Pod 實際運行版本 # 1. 確認 CD workflow 成功 gh run list --workflow=cd.yaml --limit 1 # 必須 ✅ success # 2. 驗證 Pod 鏡像版本 kubectl get pods -n awoooi-prod -o jsonpath="{.items[*].spec.containers[*].image}" # 鏡像 tag 必須與最新 commit SHA 匹配 # 3. Health check curl -f https://api.awoooi.wooo.work/api/v1/health ``` **原因:** 2026-03-24 重大事故:代碼已提交但 CD 連續失敗,正式環境仍運行舊版本,用戶誤以為功能已修復。 --- ## Alertmanager Routing **Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_alertmanager_awoooi_flow.md` ```yaml # ❌ 禁止 - 指向 OpenClaw (會使用舊 AIOPS 格式) receivers: - name: openclaw webhook_configs: - url: 'http://192.168.0.188:8088/api/v1/webhook/alertmanager' # ✅ 正確 - 指向 AWOOOI API (K3s) receivers: - name: awoooi webhook_configs: - url: 'http://192.168.0.120:32334/api/v1/webhooks/alertmanager' ``` **職責分工:** | 系統 | 職責 | Telegram 權限 | |------|------|--------------| | AWOOOI API (K3s 32334) | 告警處理 + 格式化 + 發送 | ✅ | | OpenClaw (188:8088) | AI 大腦、LLM 分析 | ❌ | **原因:** 2026-03-25 災難:Claude 錯誤將 Alertmanager 指向 OpenClaw,導致 Telegram 發送舊 AIOPS 格式(🤝 [協同警報]),而非 AWOOOI 格式(═══ 🚨 CRITICAL ═══)。 --- ## Approval Preserve Content **Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_approval_preserve_content.md` ```tsx // ❌ 禁止 - 簽核後清空內容 if (approval.status === 'approved') { return
已批准
// 原始內容不見了! } // ✅ 正確 - 保留原始內容 已批准 {/* 保留! */} ``` **原因:** 2026-03-25 統帥指示:簽核後必須保留原始告警內容,用戶需要回顧已處理的告警。 --- ## Timezone Taipei **Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_timezone_taipei.md` ```python # ❌ 禁止 - UTC 時區 from datetime import UTC datetime.now(UTC) datetime.utcnow() # ✅ 正確 - 台北時區 from src.utils.timezone import now_taipei, now_taipei_iso now_taipei() # datetime with +08:00 now_taipei_iso() # '2026-03-25T02:08:04+08:00' ``` **原因:** 統帥在台灣,日誌和 Telegram 告警必須顯示台北時間 (UTC+8),方便閱讀無需心算。 --- ## Change Annotation **Memory:** `~/.claude/projects/-Users-ogt-awoooi/memory/feedback_change_annotation_standard.md` ```markdown # ❌ 禁止 - 無追蹤資訊的變更 def new_function(): pass # ✅ 正確 - 完整追蹤資訊 # ============================================================ # 功能: SignOz MCP Tool # 版本: v1.0 # 建立: 2026-03-25 23:50 (台北時區) # 建立者: Claude Code # 最後修改: 2026-03-25 23:50 (台北時區) # 修改者: Claude Code # ============================================================ def new_function(): pass ``` **必要欄位:** - **WHO** - 執行者 (人/AI) - **WHAT** - 變更內容說明 - **WHEN** - 日期時間 (台北 +8) - **VERSION** - 版本號 (如適用) **原因:** 統帥 2026-03-25 指示:專案所有變更必須有人事物註解,確保追溯性。 --- ## AI Phase Exit Conditions > **ADR-080 鐵律 2026-04-15**: 禁止在未通過 Phase N 退出條件前宣告 Phase N 完成。 **Reference:** `docs/adr/ADR-080-ai-autonomy-flywheel-overview.md` / MASTER §5 ### 核心規則 ``` 禁止宣告「Phase N 完成」,除非: ✅ MASTER §5 Phase N 退出條件清單全部打勾 ✅ 相關測試通過(pytest 綠燈) ✅ 架構師評審 Gate N 已完成(ADR-080 §架構師評審框架) ✅ LOGBOOK 已記錄完成項目 ``` ### 7 Phase 退出條件速查 | Phase | 最關鍵退出條件 | ADR | |-------|--------------|-----| | P0 | `feature_flags.py` 建立 + `baseline_snapshot.py` 建立 + HARD_RULES v1.9 | ADR-080 | | P1 | MCP 呼叫次數/24h > 0;EvidenceSnapshot 寫入 DB | ADR-081 | | P2 | 5 Agent 全部有 unit test;Coordinator 熔斷測試通過 | ADR-082 | | P3 | 學習閉環觸發率 ≥ 99%;fire-and-forget bug 消滅 | ADR-083 | | P4 | 動態基線覆蓋率 ≥ 80%;general 告警 < 10% | ADR-084 | | P5 | Blast Radius check 100% 覆蓋;dry-run 強制通過 | ADR-085 | | P6 | SLO 計算可用;自我降級觸發後不得自動反向升級 | ADR-086 | ### 違規後果 宣告 Phase 完成但退出條件未達到 = 技術債爆炸風險,等同於在不穩定地基上繼續建樓。統帥發現違規 → 立即回滾至上一個已驗收 Phase。 --- --- ## 🔴🔴 No Island Coding — 禁止孤島開發 > 任何程式碼修改,必定影響上下游。未經全域檢視的局部最佳化,視為嚴重失職。 ### 三條鐵律 | 情境 | 必做動作 | |------|---------| | **變更函數簽名** | `grep -rn "函數名" apps/ packages/` 找出所有 Callers,一併更新 | | **變更資料模型 (DB/Pydantic)** | 同步確認 Write 層、Read 層、Redis Cache 序列化/反序列化邏輯一致 | | **任何局部修改** | 禁止「假設它會動」,必須全域掃描才能動手 | ```bash # 修改函數前的強制指令 grep -rn "target_function_name" apps/ packages/ --include="*.py" # 列出所有 Caller,逐一確認影響範圍 ``` **與積木化的關係:** No Island Coding 是「橫向」掃描(找所有呼叫者),leWOOOgo 積木化是「縱向」邊界(Router 不能越界)。兩者並行,缺一不可。 --- ## 🔴🔴🔴 Prompt-Model 同步鐵律 — LLM Schema Drift 禁令 > 血的教訓 (2026-04-17): `prompts.py` 列了 6 個 `suggested_action` 值,`models/ai.py` 只有 4 個。 > NemoTron 輸出 `"investigate"` → Pydantic ValidationError → `analysis_result = None` > → 全系統 fallback,所有 Telegram 卡片顯示「待分析」,持續數週未被察覺。 ### 強制鐵律 | 情境 | 必做動作 | |------|---------| | **修改 `prompts.py` 的 Enum/格式/欄位** | 同步檢查並更新 `models/ai.py` Pydantic Schema,確保兩者完全一致 | | **新增 LLM 輸出欄位** | 先在 Pydantic 加上 `field_validator` 含 `fallback`,再改 Prompt | | **任何接收 LLM JSON 的 Model** | 必須有 `mode="before"` validator 處理:大小寫/別名/未知值 fallback | ### 三層防護缺一不可 ```python @field_validator("suggested_action", mode="before") @classmethod def normalize_suggested_action(cls, v): if isinstance(v, str): normalized = v.upper().replace("-", "_") # 1. 別名映射 alias_map = {"DIAGNOSE": "INVESTIGATE", "MONITOR": "OBSERVE", ...} normalized = alias_map.get(normalized, normalized) # 2. 未知值 fallback — 絕不讓 Pydantic 爆炸導致 analysis_result = None try: MyEnum(normalized) return normalized except ValueError: return "NO_ACTION" # 安全預設值 return v ``` ### 禁止靜默死亡 Pydantic `ValidationError` 時,log 必須明確印出: - 哪個欄位失敗 - LLM 實際輸出了什麼值 - `pydantic_validation_failed` event(已實作於 `openclaw.py`) 違反此鐵律 = 破壞生產環境,必須立即 revert。 --- ## 🔴🔴 Proactive Execution & Circuit Breaker — 主動執行與熔斷機制 > 整合 `feedback_proactive_execution.md`(主動執行,2026-04-05)與孤島開發停問規則。 > **主動執行為絕對預設,停下問是唯一例外。** ### 預設:主動狂奔 看到任務與規格後,**一口氣實作到底**: ``` ✅ 自己寫 Code ✅ 自己跑測試 ✅ 自己修 Error ✅ 全部跑通後才回報 ``` ``` ❌ 禁止:列出步驟然後問「統帥可不可以開始」 ❌ 禁止:把「不確定」當作不寫 Code 的藉口 ❌ 禁止:拿中間的 Error 來打斷統帥 ``` ### 唯一熔斷條件(Circuit Breaker) **必須同時滿足以下兩個條件**,才可暫停並請求裁示: 1. 修改的檔案是 **Tier 3 核心紅區**(如 `decision_manager.py`、`db/base.py`) 2. 透過 `grep` 發現這個修改會直接導致 **超過 3 個以上未關聯模組**發生不可預期的 Crash 熔斷時的回報格式: ``` 發現連鎖爆炸半徑,涉及 [A, B, C],請求裁示 ``` **其餘所有情況,全部自己解決。** --- ## 🔴🔴 Self-Loop Workflow — 自循環工作流與全局回報 > 四步工作流是 AI 的**內部背景作業**,不是與統帥的來回節奏。 ### 內部自循環(不對外打斷) ``` [1. 影響評估] → [2. 實作] → [3. 串接驗證] ↓ Error [4. 自動修復] → 回到 [3. 串接驗證] ``` 遇到驗證腳本報錯,**必須自動遞迴修復,不得中斷統帥。** ### 全局單次回報(唯一一次) 當整個任務 100% 通過時,進行唯一一次成果彙報,必須包含: ``` 1. 修改了哪些檔案(逐一列出) 2. 最終成功的終端機輸出(Console Output) 3. 是否有潛在副作用 ``` 等待統帥批准後,才能執行 `git commit` 與 `push`。 ### 驗證腳本規範 — Live-Fire Only(實彈演習) 根據 [No Mock Testing](#no-mock-testing) 鐵律,`verify_script.py` **絕對禁止使用任何 Mock 套件**: ```python # ❌ 禁止 — 自己 mock 自己測,驗證毫無意義 from unittest.mock import MagicMock db = MagicMock() # ✅ 正確 — 打真實 PostgreSQL / Redis / HTTP async with get_session() as session: result = await session.execute(select(MyModel).where(...)) # 驗證完畢後必須 Teardown,清理測試資料 await session.execute(delete(MyModel).where(MyModel.id == test_id)) ``` --- ## 🟡 State & Flow Validation — 狀態機與流程驗證 修改涉及 **async、background jobs、DB 狀態轉換** 的邏輯時,必須驗證以下清單。 ### 必查斷點 | 風險類型 | 必查項目 | |---------|---------| | **中間狀態卡死** | `PENDING` 是否有機制轉為 `EXPIRED`?`PROCESSING` 超時後誰負責撿起? | | **Fire-and-forget 無聲失敗** | 異步任務失敗時,Exception 是否被吞掉?有無 dead letter / retry? | | **新增資料實體** | TTL 多長?Cleanup Job 是什麼?錯誤降級策略(Fallback)定義了嗎? | | **生命週期完整性** | 建立 → 處理 → 結案 → 清理,四個階段必須全部有對應的程式碼 | ```python # 新增資料實體的最低要求 class MyEntity(Base): expires_at: datetime # TTL 必填 # 對應 Cleanup Job: scheduler/cleanup_expired_entities.py # 降級策略: 超時 → status = EXPIRED → Telegram 告警 ``` --- ## 如何新增規則 1. 在此文件新增章節 2. 更新快速索引表 3. 在 Memory 新增對應 `feedback_*.md` 4. 更新 `MEMORY.md` 索引