diff --git a/CONSTITUTION.md b/CONSTITUTION.md index 5a3e98e..0d841eb 100644 --- a/CONSTITUTION.md +++ b/CONSTITUTION.md @@ -2,7 +2,7 @@ > 本文件定義專案開發的核心準則與不可違反的規範 > **建立日期**: 2026-01-12 -> **當前版本**: V10.26 (Active blackbox target 降噪版) +> **當前版本**: V10.27 (action_plans schema drift 修復版) > **最後更新**: 2026-04-30 --- diff --git a/app.py b/app.py index b6d7238..0c20304 100644 --- a/app.py +++ b/app.py @@ -95,8 +95,8 @@ except Exception as e: sys_log.error(f"無法檢測磁碟空間: {e}") # 🚩 系統版本定義 (備份與顯示用) -# 🚩 2026-04-30 V10.26: Active blackbox target noise reduction -SYSTEM_VERSION = "V10.26" +# 🚩 2026-04-30 V10.27: action_plans schema drift repair +SYSTEM_VERSION = "V10.27" # ========================================== # 🔒 SQL Injection 防護函數 diff --git a/config.py b/config.py index f47ebde..1b5bf15 100644 --- a/config.py +++ b/config.py @@ -254,7 +254,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.26" +SYSTEM_VERSION = "V10.27" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/database/manager.py b/database/manager.py index e521c61..aef6d73 100644 --- a/database/manager.py +++ b/database/manager.py @@ -37,6 +37,18 @@ _metadata_initialized = False _POSTGRES_METADATA_LOCK_ID = 170017 +def _repair_postgres_schema_drift(conn): + """補齊 create_all 不會處理的既有 PostgreSQL schema drift。""" + conn.execute(text(""" + ALTER TABLE IF EXISTS action_plans + ADD COLUMN IF NOT EXISTS action_type VARCHAR(100), + ADD COLUMN IF NOT EXISTS description TEXT, + ADD COLUMN IF NOT EXISTS priority INTEGER DEFAULT 3, + ADD COLUMN IF NOT EXISTS metadata_json TEXT + """)) + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_action_plans_type ON action_plans(action_type)")) + + def ensure_metadata_initialized(engine, use_postgres_lock=False): """冪等初始化 SQLAlchemy metadata,避免一般流程重複碰 DDL。""" global _metadata_initialized @@ -52,6 +64,7 @@ def ensure_metadata_initialized(engine, use_postgres_lock=False): conn.execute(text("SELECT pg_advisory_lock(:lock_id)"), {"lock_id": _POSTGRES_METADATA_LOCK_ID}) try: Base.metadata.create_all(conn) + _repair_postgres_schema_drift(conn) finally: conn.execute(text("SELECT pg_advisory_unlock(:lock_id)"), {"lock_id": _POSTGRES_METADATA_LOCK_ID}) else: diff --git a/docs/memory/ai_automation_closure_20260429.md b/docs/memory/ai_automation_closure_20260429.md index 2ea5f76..896bc87 100644 --- a/docs/memory/ai_automation_closure_20260429.md +++ b/docs/memory/ai_automation_closure_20260429.md @@ -40,6 +40,7 @@ - Scheduler 重要失敗路徑接入 EventRouter,減少裸 exception 漏通知。 - ElephantAlpha 執行引擎補 sync timeout、HITL reply_markup、未知 step fail fast、code/resource action 走 AutoHeal bridge。 - L2 `agent_actions.py` 的 `flag_for_human_review`、`route_to_km`、`mark_for_relearn` 已從 stub 改為可審計 OpenClaw memory 寫入。 +- `action_plans` 是 CodeReview / OpenClaw Group A 與 NemoTron Group B 共用表;PostgreSQL 啟動期 metadata repair 必須補齊 Group A 欄位,避免既有 DB 只有 `session_id/plan_type/sku/payload` 時讓 CodeReview action plan 寫入失敗。 - `/metrics` 已匯出 EventRouter dispatch、latency、safe action、Telegram replay、AutoHeal action 與 duration 指標。 - Smoke dashboard read-only 檢查 EventRouter queue、AutoHeal protected resources、NemoTron fallback、OpenClaw embedding queue、ElephantAlpha HITL,不做外部網路呼叫。 - Smoke history 只保存精簡紀錄,不保存完整 details,避免長期檔案膨脹與敏感資訊堆積。 @@ -69,6 +70,7 @@ - 2026-04-30 CD rebuild cutover hardening:`tests/test_cd_health_check.py` 覆蓋 build-before-stop 順序。 - 2026-04-30 ElephantAlpha NIM fallback hardening:新增 `tests/test_elephant_service.py`。 - 2026-04-30 DatabaseManager pool convergence:`tests/test_database_manager_cache.py` 覆蓋 pool size/overflow 與 engine reuse。 +- 2026-04-30 action_plans schema drift:`tests/test_database_manager_cache.py` 覆蓋 PostgreSQL repair 需補 Group A 欄位與 index。 - 2026-04-30 Ollama embedding API migration:新增 `tests/test_ollama_embedding.py`。 - 2026-04-30 Phase 3f cleanup contracts:`tests/test_phase3f_cleanup_contracts.py` 覆蓋 orphan services、env 範例、scheduler 靜默例外。 - 2026-04-30 AI metrics baseline:`tests/test_ai_automation_metrics.py` 覆蓋無事件 snapshot 仍匯出 `momo_ai_*` baseline。 diff --git a/docs/memory/history_logs.md b/docs/memory/history_logs.md index 929e500..db05e8d 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -48,6 +48,7 @@ - **EDM Dashboard endpoint 修復**: 部署後健康檢查抓到活動看板排序連結少 `edm.` blueprint 前綴,修正模板 endpoint 推導並補 5 個活動頁排序連結回歸測試。 - **Monitoring exporter 修復**: Prometheus 已能 scrape `momo-app` V10.24,但 target 檢查發現 `blackbox-exporter` 未啟動、`cadvisor` 因 host `8080` 衝突未進入 monitoring DNS;改為 cAdvisor internal-only 並補 monitoring compose 守門測試。 - **Active blackbox target 降噪**: 線上 Nginx 與 curl 驗證目前有效 MOMO 入口為 `https://mo.wooo.work`;`momo.wooo.work` 逾時、`wooo.work` DNS 不解析,先從 active UAT blackbox targets 移除,避免舊域名噪音誤導告警。 +- **action_plans schema drift 修復**: CodeReview pipeline 寫入 action plan 時發現線上表只有 NemoTron Group B 欄位;啟動期 PostgreSQL metadata repair 會補 `action_type` / `description` / `priority` / `metadata_json` 與 index,恢復 AI code review action plan 閉環。 ### 2026-04-28~29:Phase 3e 重構大戰 + daily_sales cache 隱形 bug 根除 - **app.py 縮減 -10.8%**: 7,386 → 6,590 行,11 commits 全綠零 502。 diff --git a/tests/test_database_manager_cache.py b/tests/test_database_manager_cache.py index a73205a..99eb1a7 100644 --- a/tests/test_database_manager_cache.py +++ b/tests/test_database_manager_cache.py @@ -59,3 +59,22 @@ def test_database_manager_uses_bounded_postgres_pool(monkeypatch): assert db.engine is captured["session_bind"] DatabaseManager._instance_cache.clear() + + +def test_postgres_schema_repair_adds_action_plan_group_a_columns(): + from database.manager import _repair_postgres_schema_drift + + captured = [] + + class FakeConnection: + def execute(self, statement): + captured.append(str(statement)) + + _repair_postgres_schema_drift(FakeConnection()) + ddl = "\n".join(captured) + + assert "ADD COLUMN IF NOT EXISTS action_type VARCHAR(100)" in ddl + assert "ADD COLUMN IF NOT EXISTS description TEXT" in ddl + assert "ADD COLUMN IF NOT EXISTS priority INTEGER DEFAULT 3" in ddl + assert "ADD COLUMN IF NOT EXISTS metadata_json TEXT" in ddl + assert "CREATE INDEX IF NOT EXISTS idx_action_plans_type" in ddl