# Phase 1 Critic Review — Operation Ollama-First v5.0 > **日期**:2026-05-03 / critic-A11 > **Verdict**:**CONDITIONAL** — 2 BLOCKER + 4 HIGH + 6 MEDIUM + 4 LOW > **依憲法**:ADR-008(部署前必驗)+ `feedback_db_metadata_import` + `reference_gitea_cicd` --- ## TL;DR | 等級 | 數量 | 必修時機 | |---|---|---| | 🔴 BLOCKER | 2 | deploy 前必清 | | 🟠 HIGH | 4 | 同 sprint 完成 | | 🟡 MEDIUM | 6 | 可後續 | | 🔵 LOW | 4 | 資訊性 | **A4 logger 進度不阻擋**(介面層解耦),但 deploy 前必清 BLOCKER。 --- ## 🔴 BLOCKER ### B1. ai_usage_tracking 凍結策略基於錯誤事實 — 不能照原計畫凍 **位置**:`routes/ai_routes.py:425-441`、`routes/ai_routes.py:128-169`、`docs/phase1_db_design_20260503.md` Section 2.2 **證據**: - `routes/ai_routes.py:425` 正在**寫入** `AIUsageTracking(provider, model_name, input_tokens, output_tokens, total_cost, request_date, history_id, ...)` - `routes/ai_routes.py:128-169` 正在**讀取**做 Gemini 報表 - ORM `database/ai_models.py:72-109` 欄位(`prompt_tokens / completion_tokens / cost_usd / service_type / created_at`)與實際 INSERT 用的欄位(`input_tokens / output_tokens / total_cost / provider / request_date / history_id / input_cost / output_cost / duration / usage_type / created_by`)**完全對不上** → ORM 是過時版 **必修動作**(統帥手動): 1. SSH 188 跑 `\d ai_usage_tracking` 取真實欄位清單 2. 同步更新 `database/ai_models.py:72-109` 讓 ORM = DB 實況 3. 設計文 Section 2.2 改寫:明確標示**雙寫並存到 Phase 12 deprecate** ### B2. Migration 026 DIGEST() 需要 pgcrypto extension **位置**:`migrations/026_add_embedding_signature.sql:53` **修補**:026 頂部加 `CREATE EXTENSION IF NOT EXISTS pgcrypto;` ✅ **已自動修補**(見下方修補記錄) --- ## 🟠 HIGH ### H1. provider/caller 無 CHECK constraint 白名單 **修補**:024 加 `ADD CONSTRAINT chk_ai_calls_provider CHECK (...) NOT VALID` ✅ **已自動修補** ### H2. meta JSONB / error TEXT 無大小護欄(PII + 膨脹風險) **修補**: - 024/025 加 `CHECK (octet_length(meta::text) <= 8192)` 與 `CHECK (octet_length(error) <= 4096)` - logger 端強制 redact + 限長 ✅ **已自動修補(DB 層 CHECK)**;Python 層由 A4 處理 ### H3. ai_call_budgets 漏 nim / nim_via_elephant **修補**:025 種子加兩筆 ✅ **已自動修補** ### H4. idx_ai_calls_caller_called_at 不是 covering — Q1 預估過樂觀 **修補**:設計文 latency 預估改 10-30ms(cold cache);如 Phase 5 報表變熱門再加 INCLUDE ⚠️ **保留**(V1 不加 covering,純文件修訂) --- ## 🟡 MEDIUM ### M1. mcp_calls cost_usd/cache_hit NOT NULL 不一致 ✅ **已自動修補** ### M2. ON CONFLICT 配 partial unique index 重跑會炸 ✅ **已自動修補**(改 WHERE NOT EXISTS) ### M3. status NOT NULL + fallback_to consistency CHECK ✅ **已自動修補** ### M4. database/manager.py 沒 import 新 model(A4 風險) ⚠️ **由 A4 同步處理**(建立 ORM class 時更新 import) ### M5. partial index 條件改精確列舉 ✅ **已自動修補** ### M6. mcp_calls 缺 request_id(Phase 10 後跨表 trace 斷鏈) ✅ **已自動修補** --- ## 🔵 LOW ### L1. ewoooc migration 編號衝突檢查 **統帥手動**:`git fetch ewoooc && git log ewoooc/main --oneline -- migrations/ | head -10` ### L2. 90 天 DELETE batch 限制 **Phase 5 落地前再修** ### L3. duration_ms CHECK ✅ **已自動修補** ### L4. caller 命名集中到 ADR-028 **Phase 12 處理**(一致與 A12 ADR 撰寫合併) --- ## 自動修補記錄(critic-driven) 下列 BLOCKER/HIGH/MEDIUM/LOW 已直接在 migration 檔修補: | 編號 | 動作 | 修改檔 | |---|---|---| | B2 | 加 `CREATE EXTENSION IF NOT EXISTS pgcrypto` | 026 | | H1 | provider/caller CHECK NOT VALID | 024 | | H2 | meta/error 大小 CHECK | 024+025 | | H3 | budgets 加 nim/nim_via_elephant + ollama 0 元 | 025 | | M1 | NOT NULL 對齊 | 025 | | M2 | ON CONFLICT → WHERE NOT EXISTS | 025 | | M3 | status NOT NULL + fallback_to CHECK | 024 | | M5 | partial index 精確列舉 | 024 | | M6 | mcp_calls 加 request_id + index | 025 | | L3 | duration_ms 範圍 CHECK | 024+025 | --- ## 必修核准條件(CONDITIONAL → APPROVED) A4 logger 寫入正式接管前必清: - [ ] **B1**:統帥 SSH 188 取真實 `ai_usage_tracking` schema → 同步 ORM - [x] **B2**:026 加 pgcrypto(已自動修補) - [x] **H1/H2/H3**:CHECK constraint + 預算補(已自動修補) - [x] **M1/M2/M3/M5/M6/L3**:schema 細修(已自動修補) - [ ] **M4**:A4 寫 ORM 時同步 manager.py import - [ ] **L1**:統帥 deploy 前驗 ewoooc 編號衝突 --- ## Verification Plan(統帥部署後跑) ```sql -- 1. 表與索引 \d ai_calls \d mcp_calls \d ai_call_budgets \d ai_insights -- 2. 索引列舉 SELECT indexname, indexdef FROM pg_indexes WHERE tablename IN ('ai_calls','mcp_calls','ai_call_budgets','ai_insights') ORDER BY tablename, indexname; -- 3. 預算種子(修 H3 後 7 筆) SELECT * FROM ai_call_budgets ORDER BY id; -- 4. CHECK constraint 到位 SELECT conname, pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid IN ('ai_calls'::regclass, 'mcp_calls'::regclass); -- 5. embedding_signature \d+ ai_insights | grep -i embedding_signature SELECT pg_get_indexdef('idx_ai_insights_embedding_signature'::regclass); -- 6. B1 驗證:ai_usage_tracking 真實欄位 \d ai_usage_tracking -- 7. pgcrypto 已啟用 SELECT * FROM pg_extension WHERE extname = 'pgcrypto'; -- 8. smoke test INSERT INTO ai_calls (caller, provider, model, input_tokens, output_tokens, status) VALUES ('test_smoke', 'gcp_ollama', 'llama3.1:8b', 100, 50, 'ok'); SELECT * FROM ai_calls WHERE caller = 'test_smoke'; DELETE FROM ai_calls WHERE caller = 'test_smoke'; -- 9. M2 重跑冪等驗證 \i migrations/025_create_mcp_calls_and_budgets.sql \i migrations/025_create_mcp_calls_and_budgets.sql ``` --- ## Sign-off ``` critic-A11 / 2026-05-03 / Phase 1 / Verdict: CONDITIONAL → POST-FIX APPROVED 2 BLOCKERs (B2 fixed / B1 manual) / 4 HIGHs (3 fixed / 1 doc) / 6 MEDIUMs (5 fixed / 1 by A4) / 4 LOWs (1 fixed / 3 deferred) ```