fix(governance): cast dispatch status filter
This commit is contained in:
@@ -275,14 +275,14 @@ async def _query_dispatch_table(
|
||||
NULL::text AS operator_note
|
||||
FROM governance_remediation_dispatch d
|
||||
JOIN ai_governance_events e ON e.id = d.governance_event_id
|
||||
WHERE d.dispatch_status = :dispatch_status
|
||||
WHERE d.dispatch_status = CAST(:dispatch_status AS governance_dispatch_status)
|
||||
ORDER BY d.dispatched_at DESC
|
||||
""")
|
||||
|
||||
count_sql = text("""
|
||||
SELECT count(*) AS cnt
|
||||
FROM governance_remediation_dispatch
|
||||
WHERE dispatch_status = :dispatch_status
|
||||
WHERE dispatch_status = CAST(:dispatch_status AS governance_dispatch_status)
|
||||
""")
|
||||
|
||||
async with get_db_context() as db:
|
||||
|
||||
@@ -288,6 +288,7 @@ class TestQueueEndpoint:
|
||||
assert "d.dispatched_at AS created_at" in source
|
||||
assert "ORDER BY d.dispatched_at DESC" in source
|
||||
assert "NULL::text AS operator_note" in source
|
||||
assert "CAST(:dispatch_status AS governance_dispatch_status)" in source
|
||||
assert "d.created_at" not in source
|
||||
assert "d.operator_note" not in source
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
**根因**:
|
||||
- `governance/events`:production `ai_governance_events.details.remediation` 已有 dict 形態,例如 `{"items": [...]}`;read model `GovernanceEvent.remediation` 期待字串,Pydantic validation 造成 500。
|
||||
- `governance/queue`:查詢仍讀 `governance_remediation_dispatch.created_at` / `operator_note`,但 production migration schema 實際是 `dispatched_at` / `created_by`,沒有 `created_at` / `operator_note`。
|
||||
- `governance/queue`:第二層 production 差異是 `dispatch_status` 為 PostgreSQL enum;SQLAlchemy 參數被送成 varchar,未明確 cast 時 Postgres 會拒絕 enum = varchar 比較,導致真表被誤判成 `table_pending=true`。
|
||||
|
||||
**修正**:
|
||||
- `governance_query_service._extract_remediation()` 將 `details.remediation` 的 string / dict / list 正規化成短字串,避免歷史治理事件破壞 response schema。
|
||||
- `_to_governance_event()` 對非 dict details 做 read-side guard。
|
||||
- `_query_dispatch_table()` 對齊 production schema:以 `d.dispatched_at AS created_at`、`NULL::text AS operator_note` 相容現有前端 DTO,不改 DB schema。
|
||||
- `_query_dispatch_table()` 對 `dispatch_status` 明確 `CAST(:dispatch_status AS governance_dispatch_status)`,避免 enum/varchar 比較錯誤。
|
||||
- 補測 `test_ai_governance_endpoints.py`,覆蓋 dict remediation normalization 與 queue 查詢欄位相容性。
|
||||
|
||||
**本地驗證**:
|
||||
|
||||
@@ -2119,8 +2119,8 @@ Phase 6 完成後
|
||||
|
||||
**T17b Governance API 紅燈修復(2026-05-14 台北)**:
|
||||
- 觸發:T17A 前端工作鏈路已能顯示治理卡點,但 live smoke 發現 `governance/events` 500、`governance/queue` 回 `table_pending=true`,會讓 Operator Console 無法可信呈現治理告警是否被 dispatch、跳過、修復或卡人工。
|
||||
- 根因:`ai_governance_events.details.remediation` 在 production 已有 dict/list 形態,read model 仍只收字串;`governance_remediation_dispatch` production schema 使用 `dispatched_at`,查詢卻讀不存在的 `created_at` / `operator_note`。
|
||||
- 修正:read-side normalization 將 remediation string/dict/list 正規化成短字串;queue query 改用 `d.dispatched_at AS created_at` 與 `NULL::text AS operator_note` 相容既有 DTO,不改 DB schema。
|
||||
- 根因:`ai_governance_events.details.remediation` 在 production 已有 dict/list 形態,read model 仍只收字串;`governance_remediation_dispatch` production schema 使用 `dispatched_at`,查詢卻讀不存在的 `created_at` / `operator_note`;第二層差異是 `dispatch_status` 為 PostgreSQL enum,未明確 cast 時會被 asyncpg 以 varchar 參數送入。
|
||||
- 修正:read-side normalization 將 remediation string/dict/list 正規化成短字串;queue query 改用 `d.dispatched_at AS created_at` 與 `NULL::text AS operator_note` 相容既有 DTO,並以 `CAST(:dispatch_status AS governance_dispatch_status)` 對齊 enum,不改 DB schema。
|
||||
- 驗證:`py_compile` pass;`pytest tests/test_ai_governance_endpoints.py tests/test_governance_remediation_dispatch.py -q` 53 passed;ruff F/E9 pass;diff check pass。
|
||||
- 目前進度更新:Alertmanager 低風險自動修復主線約 96%;完整 AI 自動化管理產品化約 75%。T17B 推版後,下一段收斂 governance dispatcher skipped reason / leader-dedupe / SLO emitter。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user