diff --git a/config.py b/config.py index 0f27613..c5929f3 100644 --- a/config.py +++ b/config.py @@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.741" +SYSTEM_VERSION = "V10.742" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md index 274e7de..97da548 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -1,8 +1,8 @@ # PChome 業績成長自動化作戰系統 — AI 競價情報模組 Single Source of Truth > **最後更新**: 2026-07-03 (台北時間) -> **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯;PChome 後台業績匯入韌性已補強;產品定位正名為「PChome 業績成長自動化作戰系統」;外部市場來源正規化層、自動同步、作戰清單與價格參考表優先讀取、CSV 備援預檢、前台操作入口、高可見頁面繁中化守門、比價/作戰 UI 工作台化、AI 密集工作台文字密度守門、跨平台來源治理、商品身份 UI 契約、sitewide visual QA runtime readback、PChome auto-policy authorization guard monitoring、decision preflight machine evidence monitoring、decision closeout monitoring、authorization issuer gate monitoring、signing decision preflight monitoring、signing decision closeout monitoring、signing issuer guard monitoring、signing issuer closeout monitoring、signing execution preflight monitoring、signing execution closeout monitoring、signed receipt preflight monitoring、signed receipt closeout monitoring、signed receipt evidence intake monitoring、detached verification evidence validation monitoring、verifier receipt closeout monitoring 與 authorization evidence execution preflight monitoring 已建立,GCP embedding 熔斷延後處理、110 proxy rescue 與 direct host health skip 已建立 -> **適用版本**: V10.741 +> **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯;PChome 後台業績匯入韌性已補強;產品定位正名為「PChome 業績成長自動化作戰系統」;外部市場來源正規化層、自動同步、作戰清單與價格參考表優先讀取、CSV 備援預檢、前台操作入口、高可見頁面繁中化守門、比價/作戰 UI 工作台化、AI 密集工作台文字密度守門、跨平台來源治理、商品身份 UI 契約、sitewide visual QA runtime readback、PChome auto-policy authorization guard monitoring、decision preflight machine evidence monitoring、decision closeout monitoring、authorization issuer gate monitoring、signing decision preflight monitoring、signing decision closeout monitoring、signing issuer guard monitoring、signing issuer closeout monitoring、signing execution preflight monitoring、signing execution closeout monitoring、signed receipt preflight monitoring、signed receipt closeout monitoring、signed receipt evidence intake monitoring、detached verification evidence validation monitoring、verifier receipt closeout monitoring、authorization evidence execution preflight monitoring 與 authorization evidence execution closeout monitoring 已建立,GCP embedding 熔斷延後處理、110 proxy rescue 與 direct host health skip 已建立 +> **適用版本**: V10.742 --- @@ -867,6 +867,7 @@ POSTGRES_HOST=momo-db | 2026-07-03 | PChome auto-policy detached verification evidence validation 必須有 runtime monitoring | V10.739 起 `/api/ai-automation/smoke`、`/api/ai-automation/scheduled-health-summary` 與 `/metrics` 必須輸出 `PChome auto-policy detached verification evidence validation` / `pchome_auto_policy_detached_verification_evidence_validation`;此 runtime check 自動驗證 verifier receipt closeout boundary、12 個 verifier receipt fields、10 個 verifier receipt acceptance gates、12 個 validation checks、10 個 required external receipt evidence、8 個 external receipt acceptance gates、10 個 detached receipt verification checks、post-apply verifier requirement 與 same-run production truth requirement,但明確標記 `reads_secret_count=0`、`writes_database_count=0`、`signs_database_apply_authorization_count=0`、`primary_human_gate_count=0`、`requires_detached_signature_verification=true`、`detached_signature_verification_performed=false`、`verifier_receipt_persisted=false`、`external_signed_authorization_receipt_included=false`、`signed_authorization_receipt_included=false`、`signature_material_included=false`、`accepts_plaintext_secret=false`、`ready_for_database_apply_now=false`、`issues_database_apply_authorization=false`、`signs_database_apply_authorization=false`、`secret_material_included=false`、`secret_material_required_in_preview=false`,不讀 secret、不執行 detached verification、不持久化 verifier receipt、不執行 SQL、不寫 DB、不含 signed receipt 或 signature material、不接受 plaintext secret、不簽發 apply authorization。 | | 2026-07-03 | PChome auto-policy verifier receipt closeout 必須有 runtime monitoring | V10.740 起 `/api/ai-automation/smoke`、`/api/ai-automation/scheduled-health-summary` 與 `/metrics` 必須輸出 `PChome auto-policy verifier receipt closeout` / `pchome_auto_policy_verifier_receipt_closeout`;此 runtime check 自動驗證 verifier receipt evidence handoff、12 個 handoff fields、10 個 handoff acceptance gates、12 個 closeout checks、12 個 detached verification evidence validation checks、10 個 required external receipt evidence、8 個 external receipt acceptance gates、post-apply verifier requirement 與 same-run production truth requirement,但明確標記 `reads_secret_count=0`、`executes_script_count=0`、`executes_sql_count=0`、`writes_database_count=0`、`signs_database_apply_authorization_count=0`、`primary_human_gate_count=0`、`requires_detached_signature_verification=true`、`detached_signature_verification_performed=false`、`verifier_receipt_persisted=false`、`external_signed_authorization_receipt_included=false`、`signed_authorization_receipt_included=false`、`signature_material_included=false`、`accepts_plaintext_secret=false`、`ready_for_database_apply_now=false`、`issues_database_apply_authorization=false`、`signs_database_apply_authorization=false`、`secret_material_included=false`、`secret_material_required_in_preview=false`,不讀 secret、不執行 detached verification、不持久化 verifier receipt、不執行 SQL、不寫 DB、不含 signed receipt 或 signature material、不接受 plaintext secret、不簽發 apply authorization。 | | 2026-07-03 | PChome auto-policy authorization evidence execution preflight 必須有 runtime monitoring | V10.741 起 `/api/ai-automation/smoke`、`/api/ai-automation/scheduled-health-summary` 與 `/metrics` 必須輸出 `PChome auto-policy authorization evidence execution preflight` / `pchome_auto_policy_authorization_evidence_execution_preflight`;此 runtime check 自動驗證 future database apply authorization verifier handoff、authorization evidence execution preflight package、12 個 preflight checks、12 個 execution fields、10 個 execution acceptance gates、verifier receipt closeout carry-forward、same-run production truth requirement 與 post-apply verifier requirement,但明確標記 `reads_secret_count=0`、`executes_script_count=0`、`executes_sql_count=0`、`writes_database_count=0`、`signs_database_apply_authorization_count=0`、`primary_human_gate_count=0`、`executes_authorization_evidence=false`、`requires_detached_signature_verification=true`、`detached_signature_verification_performed=false`、`verifier_receipt_persisted=false`、`external_signed_authorization_receipt_included=false`、`signed_authorization_receipt_included=false`、`signature_material_included=false`、`accepts_plaintext_secret=false`、`ready_for_database_apply_now=false`、`issues_database_apply_authorization=false`、`signs_database_apply_authorization=false`、`secret_material_included=false`、`secret_material_required_in_preview=false`,不讀 secret、不執行 authorization evidence、不執行 detached verification、不持久化 verifier receipt、不執行 SQL、不寫 DB、不含 signed receipt 或 signature material、不接受 plaintext secret、不簽發 apply authorization。 | +| 2026-07-03 | PChome auto-policy authorization evidence execution closeout 必須有 runtime monitoring | V10.742 起 `/api/ai-automation/smoke`、`/api/ai-automation/scheduled-health-summary` 與 `/metrics` 必須輸出 `PChome auto-policy authorization evidence execution closeout` / `pchome_auto_policy_authorization_evidence_execution_closeout`;此 runtime check 自動驗證 future database apply authorization final verifier gate、authorization evidence execution closeout package、12 個 closeout checks、12 個 closeout fields、10 個 closeout acceptance gates、authorization evidence execution preflight carry-forward、verifier receipt closeout carry-forward、same-run production truth requirement 與 post-apply verifier requirement,但明確標記 `reads_secret_count=0`、`executes_script_count=0`、`executes_sql_count=0`、`writes_database_count=0`、`signs_database_apply_authorization_count=0`、`primary_human_gate_count=0`、`executes_authorization_evidence=false`、`executes_database_apply=false`、`database_apply_authorized=false`、`requires_detached_signature_verification=true`、`detached_signature_verification_performed=false`、`verifier_receipt_persisted=false`、`external_signed_authorization_receipt_included=false`、`signed_authorization_receipt_included=false`、`signature_material_included=false`、`accepts_plaintext_secret=false`、`ready_for_database_apply_now=false`、`issues_database_apply_authorization=false`、`signs_database_apply_authorization=false`、`secret_material_included=false`、`secret_material_required_in_preview=false`,不讀 secret、不執行 authorization evidence、不執行 database apply、不執行 detached verification、不持久化 verifier receipt、不執行 SQL、不寫 DB、不含 signed receipt 或 signature material、不接受 plaintext secret、不簽發 apply authorization。 | | 2026-06-29 | PChome DB apply 授權 lane 必須先通過 no-write guard / decision preflight / decision closeout / issuer gate / signing-decision preflight / signing-decision closeout / signing-issuer guard | V10.725 的 PChome mapping backlog auto-policy 已新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-lane-guard`、`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-preflight`、`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-closeout`、`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-issuer-gate`、`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-preflight`、`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-closeout` 與 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-guard`;這些 endpoint 只驗證 final exact request package、same-run production truth requirement、secret rejection、rollback boundary、lane entry requirements、decision input requirements、rejection policy、post-apply verifier、future authorization decision package、final nonsecret authorization envelope、signing decision preflight inputs、unsigned signing decision package 與 signable request boundary,不讀 secret、不執行 shell/SQL、不寫 DB,也不簽發 database apply authorization。 | | 2026-06-29 | PChome DB apply 授權簽署發行者 lane 必須先產出 final signable request package | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout`;此 endpoint 只把 signing-issuer guard 的 signable request boundary 收斂成 final signable request package 與 closeout contract,確認 fresh production truth、post-apply verifier、migration hash、secret boundary 與 no-side-effect checks,不讀 secret、不簽發 authorization、不執行 shell/SQL、不寫 DB,也不代表正式 DB apply 已授權。 | | 2026-06-29 | PChome DB apply 授權簽署執行 lane 必須先通過 operator-held secret boundary preflight | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight`;此 endpoint 只把 final signable request package 轉成 future signing execution preflight package、operator-held secret boundary contract、nonsecret signing inputs、command-shape preview、rollback boundary 與 abort conditions,不讀 secret、不接受 plaintext secret、不簽發 authorization、不執行 shell/SQL、不寫 DB,也不代表正式 DB apply 已授權。 | diff --git a/docs/guides/pchome_ai_automation_priority_backlog.md b/docs/guides/pchome_ai_automation_priority_backlog.md index 7bc18dd..aac5320 100644 --- a/docs/guides/pchome_ai_automation_priority_backlog.md +++ b/docs/guides/pchome_ai_automation_priority_backlog.md @@ -9,7 +9,7 @@ 以下是使用者在主線推進期間插入、且必須保留在完整工作項目裡的要求。後續不得再把這些需求漏掉或變成口頭提醒。 1. 正式環境才是最新版本真相;不得用本機檔案、舊筆記、舊分支蓋過 production truth。 -2. 版本不得搞錯;目前正式環境 `/health` 版本維持 `V10.741`,除非有明確版本 bump、部署與 readback。 +2. 版本不得搞錯;目前正式環境 `/health` 版本推進目標為 `V10.742`,完成條件必須包含明確版本 bump、部署與 readback。 3. GitHub 全面 freeze;不得使用 GitHub、`gh`、GitHub API、GitHub Actions、PR、issue、mirror 或 read-only GitHub 流程。 4. 實作結果必須推到 Gitea `dev` 與 `main`;若改到 runtime 行為,還必須部署到正式環境並回讀。 5. 主方向是 AI 自動化,不是人工審核;人工欄位只能當 evidence / ledger / UI truth,不得阻擋低爆炸半徑、可驗證、可回滾的 controlled apply。 @@ -41,7 +41,7 @@ 已完成: -- 正式環境版本真相 guard:`/health` 是最高真相,目前維持 `V10.741`。 +- 正式環境版本真相 guard:`/health` 是最高真相,本輪推進目標為 `V10.742`。 - Gitea `dev` / `main` source truth 對齊。 - Retry exception controlled apply executor 已落地,目標表為 `pchome_product_matches`。 - 正式 DB 已 controlled apply 4 個 selectors: @@ -197,10 +197,17 @@ - `/api/ai-automation/scheduled-health-summary` 已輸出 `pchome_auto_policy_authorization_evidence_execution_preflight` family - `/metrics` 已輸出 `momo_ai_automation_scheduled_health_family_status{family="pchome_auto_policy_authorization_evidence_execution_preflight",...}` - 不執行 authorization evidence、不執行 detached signature verification、不持久化 verifier receipt、不包含 external signed receipt、不包含 signed receipt、不包含 signature material、不接受 plaintext secret、不讀 secret、不執行 SQL、不寫 DB、不簽發 apply authorization,下一個機器動作是 authorization evidence execution closeout lane +- PChome auto-policy authorization evidence execution closeout runtime monitoring 已完成: + - `/api/ai-automation/smoke` 已加入 `PChome auto-policy authorization evidence execution closeout` + - 以本機 contract fixture + machine evidence adapter 自動推進 database apply controlled-apply final preflight path + - 明確標記 `authorization_evidence_execution_closeout_check_count=12`、`authorization_evidence_execution_closeout_pass_count=12`、`authorization_evidence_execution_closeout_field_count=12`、`authorization_evidence_execution_closeout_acceptance_gate_count=10`、`authorization_evidence_execution_preflight_ready_count=1`、`database_apply_authorization_final_verifier_gate_ready_count=1`、`verifier_receipt_evidence_handoff_count=1`、`required_external_receipt_evidence_count=10`、`external_receipt_acceptance_gate_count=8`、`writes_database_count=0`、`signs_database_apply_authorization_count=0`、`reads_secret_count=0`、`primary_human_gate_count=0` + - `/api/ai-automation/scheduled-health-summary` 已輸出 `pchome_auto_policy_authorization_evidence_execution_closeout` family + - `/metrics` 已輸出 `momo_ai_automation_scheduled_health_family_status{family="pchome_auto_policy_authorization_evidence_execution_closeout",...}` + - 不執行 authorization evidence、不執行 database apply、不執行 detached signature verification、不持久化 verifier receipt、不包含 external signed receipt、不包含 signed receipt、不包含 signature material、不接受 plaintext secret、不讀 secret、不執行 SQL、不寫 DB、不簽發 apply authorization,下一個機器動作是 controlled-apply final preflight lane 進行中 / 下一步,必須照順序: -1. 將 auto-policy authorization evidence execution preflight 往 authorization evidence execution closeout lane 推進。 +1. 將 auto-policy authorization evidence execution closeout 往 controlled-apply final preflight lane 推進。 完成標準: @@ -367,7 +374,7 @@ 進行中 / 下一步,必須照順序: -1. 將 auto-policy authorization evidence execution preflight 推進到 authorization evidence execution closeout lane。 +1. 將 auto-policy authorization evidence execution closeout 推進到 controlled-apply final preflight lane。 2. 對其他 safe mapping backlog lanes 套用 receipt / replay / drift verifier 模式。 3. 為每一類 controlled apply 自動產生 rollback evidence package。 4. 把 AI automation metrics 持續整合進既有 observability surfaces。 @@ -454,7 +461,8 @@ | P3.17 | PChome auto-policy signed receipt evidence intake monitoring | 已完成 | AI smoke + scheduled family + metrics family prove detached verification evidence schema, 12 evidence fields, 10 detached acceptance gates, no detached verification execution, no signed receipt, no signature material, no plaintext secret, no secret read, no DB write, no signing | P3.18 detached verification evidence validation monitoring | | P3.18 | PChome auto-policy detached verification evidence validation monitoring | 已完成 | AI smoke + scheduled family + metrics family prove verifier receipt closeout boundary, 12 verifier receipt fields, 10 verifier acceptance gates, no detached verification execution, no verifier receipt persist, no signed receipt, no signature material, no plaintext secret, no secret read, no DB write, no signing | P3.19 verifier receipt closeout monitoring | | P3.19 | PChome auto-policy verifier receipt closeout monitoring | 已完成 | AI smoke + scheduled family + metrics family prove verifier receipt evidence handoff, 12 handoff fields, 10 handoff acceptance gates, no detached verification execution, no verifier receipt persist, no signed receipt, no signature material, no plaintext secret, no secret read, no DB write, no signing | P3.20 authorization evidence execution preflight monitoring | -| P3.20 | PChome auto-policy authorization evidence execution preflight monitoring | 已完成 | AI smoke + scheduled family + metrics family prove database apply authorization verifier handoff, 12 execution fields, 10 execution acceptance gates, no authorization evidence execution, no detached verification execution, no verifier receipt persist, no signed receipt, no signature material, no plaintext secret, no secret read, no DB write, no signing | P3.21 authorization evidence execution closeout lane | +| P3.20 | PChome auto-policy authorization evidence execution preflight monitoring | 已完成 | AI smoke + scheduled family + metrics family prove database apply authorization verifier handoff, 12 execution fields, 10 execution acceptance gates, no authorization evidence execution, no detached verification execution, no verifier receipt persist, no signed receipt, no signature material, no plaintext secret, no secret read, no DB write, no signing | P3.21 authorization evidence execution closeout monitoring | +| P3.21 | PChome auto-policy authorization evidence execution closeout monitoring | 已完成 | AI smoke + scheduled family + metrics family prove database apply authorization final verifier gate, 12 closeout fields, 10 closeout acceptance gates, no authorization evidence execution, no database apply, no detached verification execution, no verifier receipt persist, no signed receipt, no signature material, no plaintext secret, no secret read, no DB write, no signing | P3.22 controlled-apply final preflight monitoring | | P4.1 | Source / deployment / runtime truth package | 已完成 | `scripts/ops/report_source_deploy_runtime_truth.py` + focused tests | 每次 Gitea push / production deploy 後執行 P4 report | | P2.2 | Benchmark guardrails applied to core AI surfaces | 已完成 | `/ai_intelligence` + `/observability/overview` golden-signal strips + focused tests | 後續 safe automation lanes 套同樣 first-viewport summary | diff --git a/services/ai_automation_smoke_service.py b/services/ai_automation_smoke_service.py index 0858168..4665731 100644 --- a/services/ai_automation_smoke_service.py +++ b/services/ai_automation_smoke_service.py @@ -433,6 +433,23 @@ def build_scheduled_automation_health_summary( auto_policy_authorization_evidence_preflight_details = ( auto_policy_authorization_evidence_preflight.get("details") or {} ) + auto_policy_authorization_evidence_closeout = _find_check( + source_result, + "PChome auto-policy authorization evidence execution closeout", + ) + auto_policy_authorization_evidence_closeout_details = ( + auto_policy_authorization_evidence_closeout.get("details") or {} + ) + if ( + not auto_policy_authorization_evidence_closeout + or not auto_policy_authorization_evidence_closeout_details + ): + auto_policy_authorization_evidence_closeout = ( + _pchome_auto_policy_authorization_evidence_execution_closeout_check() + ) + auto_policy_authorization_evidence_closeout_details = ( + auto_policy_authorization_evidence_closeout.get("details") or {} + ) surface_readback = _find_check(source_result, "AI surface HTML readback") surface_details = surface_readback.get("details") or {} sitewide_readback = _find_check(source_result, "Sitewide UI/UX Agent readback") @@ -3525,6 +3542,372 @@ def build_scheduled_automation_health_summary( "writes_database": False, }, }, + { + "key": "pchome_auto_policy_authorization_evidence_execution_closeout", + "label": "PChome auto-policy authorization evidence execution closeout", + "status": ( + auto_policy_authorization_evidence_closeout.get("status") + or "warning" + ), + "summary": ( + auto_policy_authorization_evidence_closeout.get("summary") + or ( + "PChome auto-policy authorization evidence execution " + "closeout has no latest check." + ) + ), + "next_machine_action": ( + auto_policy_authorization_evidence_closeout_details.get( + "next_machine_action" + ) + or ( + "continue_pchome_auto_policy_authorization_evidence_execution_closeout_monitoring" + if auto_policy_authorization_evidence_closeout.get("status") + == "ok" + else "refresh_pchome_auto_policy_authorization_evidence_execution_closeout" + ) + ), + "details": { + "policy": auto_policy_authorization_evidence_closeout_details.get( + "policy" + ), + "result": auto_policy_authorization_evidence_closeout_details.get( + "result" + ), + "authorization_evidence_execution_closeout_ready_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_closeout_ready_count" + ) + or 0 + ), + "authorization_evidence_execution_closeout_check_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_closeout_check_count" + ) + or 0 + ), + "authorization_evidence_execution_closeout_pass_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_closeout_pass_count" + ) + or 0 + ), + "authorization_evidence_execution_closeout_waiting_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_closeout_waiting_count" + ) + or 0 + ), + "authorization_evidence_execution_preflight_ready_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_preflight_ready_count" + ) + or 0 + ), + "authorization_evidence_execution_preflight_check_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_preflight_check_count" + ) + or 0 + ), + "authorization_verifier_receipt_closeout_ready_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_verifier_receipt_closeout_ready_count" + ) + or 0 + ), + "verifier_receipt_closeout_check_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "verifier_receipt_closeout_check_count" + ) + or 0 + ), + "detached_verification_evidence_validation_check_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "detached_verification_evidence_validation_check_count" + ) + or 0 + ), + "verifier_receipt_evidence_handoff_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "verifier_receipt_evidence_handoff_count" + ) + or 0 + ), + "authorization_evidence_execution_preflight_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_preflight_count" + ) + or 0 + ), + "authorization_evidence_execution_closeout_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_closeout_count" + ) + or 0 + ), + "database_apply_final_verifier_gate_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "database_apply_final_verifier_gate_count" + ) + or 0 + ), + "database_apply_authorization_final_verifier_gate_ready_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "database_apply_authorization_final_verifier_gate_ready_count" + ) + or 0 + ), + "authorization_evidence_execution_closeout_field_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_closeout_field_count" + ) + or 0 + ), + "authorization_evidence_execution_closeout_acceptance_gate_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_closeout_acceptance_gate_count" + ) + or 0 + ), + "authorization_evidence_execution_field_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_field_count" + ) + or 0 + ), + "authorization_evidence_execution_acceptance_gate_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_acceptance_gate_count" + ) + or 0 + ), + "verifier_receipt_field_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "verifier_receipt_field_count" + ) + or 0 + ), + "verifier_receipt_acceptance_gate_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "verifier_receipt_acceptance_gate_count" + ) + or 0 + ), + "verifier_receipt_evidence_handoff_field_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "verifier_receipt_evidence_handoff_field_count" + ) + or 0 + ), + "verifier_receipt_handoff_acceptance_gate_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "verifier_receipt_handoff_acceptance_gate_count" + ) + or 0 + ), + "required_external_receipt_evidence_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "required_external_receipt_evidence_count" + ) + or 0 + ), + "external_receipt_acceptance_gate_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "external_receipt_acceptance_gate_count" + ) + or 0 + ), + "post_apply_verifier_required_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "post_apply_verifier_required_count" + ) + or 0 + ), + "same_run_truth_required_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "same_run_truth_required_count" + ) + or 0 + ), + "reads_secret_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "reads_secret_count" + ) + or 0 + ), + "executes_script_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "executes_script_count" + ) + or 0 + ), + "executes_sql_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "executes_sql_count" + ) + or 0 + ), + "writes_database_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "writes_database_count" + ) + or 0 + ), + "signs_database_apply_authorization_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "signs_database_apply_authorization_count" + ) + or 0 + ), + "primary_human_gate_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "primary_human_gate_count" + ) + or 0 + ), + "manual_review_required_count": int( + auto_policy_authorization_evidence_closeout_details.get( + "manual_review_required_count" + ) + or 0 + ), + "manual_review_mode": ( + auto_policy_authorization_evidence_closeout_details.get( + "manual_review_mode" + ) + ), + "payload_source": auto_policy_authorization_evidence_closeout_details.get( + "payload_source" + ), + "execute_fetch": bool( + auto_policy_authorization_evidence_closeout_details.get( + "execute_fetch" + ) + ), + "outbound_network": bool( + auto_policy_authorization_evidence_closeout_details.get( + "outbound_network" + ) + ), + "business_data_source": bool( + auto_policy_authorization_evidence_closeout_details.get( + "business_data_source" + ) + ), + "ready_for_future_database_apply_authorization_final_verifier_gate": bool( + auto_policy_authorization_evidence_closeout_details.get( + "ready_for_future_database_apply_authorization_final_verifier_gate" + ) + ), + "can_enter_future_database_apply_controlled_apply_final_preflight": bool( + auto_policy_authorization_evidence_closeout_details.get( + "can_enter_future_database_apply_controlled_apply_final_preflight" + ) + ), + "authorization_evidence_execution_closeout_ready": bool( + auto_policy_authorization_evidence_closeout_details.get( + "authorization_evidence_execution_closeout_ready" + ) + ), + "ready_for_future_authorization_evidence_execution_closeout": bool( + auto_policy_authorization_evidence_closeout_details.get( + "ready_for_future_authorization_evidence_execution_closeout" + ) + ), + "permits_future_database_apply_authorization_final_verifier_gate": bool( + auto_policy_authorization_evidence_closeout_details.get( + "permits_future_database_apply_authorization_final_verifier_gate" + ) + ), + "permits_future_database_apply_controlled_apply_final_preflight": bool( + auto_policy_authorization_evidence_closeout_details.get( + "permits_future_database_apply_controlled_apply_final_preflight" + ) + ), + "requires_detached_signature_verification": bool( + auto_policy_authorization_evidence_closeout_details.get( + "requires_detached_signature_verification" + ) + ), + "detached_signature_verification_performed": bool( + auto_policy_authorization_evidence_closeout_details.get( + "detached_signature_verification_performed" + ) + ), + "verifier_receipt_persisted": bool( + auto_policy_authorization_evidence_closeout_details.get( + "verifier_receipt_persisted" + ) + ), + "external_signed_authorization_receipt_included": bool( + auto_policy_authorization_evidence_closeout_details.get( + "external_signed_authorization_receipt_included" + ) + ), + "signed_authorization_receipt_included": bool( + auto_policy_authorization_evidence_closeout_details.get( + "signed_authorization_receipt_included" + ) + ), + "signature_material_included": bool( + auto_policy_authorization_evidence_closeout_details.get( + "signature_material_included" + ) + ), + "secret_material_included": bool( + auto_policy_authorization_evidence_closeout_details.get( + "secret_material_included" + ) + ), + "secret_material_required_in_preview": bool( + auto_policy_authorization_evidence_closeout_details.get( + "secret_material_required_in_preview" + ) + ), + "accepts_plaintext_secret": bool( + auto_policy_authorization_evidence_closeout_details.get( + "accepts_plaintext_secret" + ) + ), + "reads_secret_in_preview": bool( + auto_policy_authorization_evidence_closeout_details.get( + "reads_secret_in_preview" + ) + ), + "executes_authorization_evidence": bool( + auto_policy_authorization_evidence_closeout_details.get( + "executes_authorization_evidence" + ) + ), + "executes_database_apply": bool( + auto_policy_authorization_evidence_closeout_details.get( + "executes_database_apply" + ) + ), + "ready_for_database_apply_now": bool( + auto_policy_authorization_evidence_closeout_details.get( + "ready_for_database_apply_now" + ) + ), + "database_apply_authorized": bool( + auto_policy_authorization_evidence_closeout_details.get( + "database_apply_authorized" + ) + ), + "issues_database_apply_authorization": bool( + auto_policy_authorization_evidence_closeout_details.get( + "issues_database_apply_authorization" + ) + ), + "signs_database_apply_authorization": bool( + auto_policy_authorization_evidence_closeout_details.get( + "signs_database_apply_authorization" + ) + ), + "writes_database": False, + }, + }, { "key": "ai_surface_html_readback", "label": "AI surface HTML readback", @@ -9001,6 +9384,606 @@ def _pchome_auto_policy_authorization_evidence_execution_preflight_check() -> Di ) +def _pchome_auto_policy_authorization_evidence_execution_closeout_check() -> Dict[str, Any]: + """Read-only monitor for the future authorization evidence execution closeout.""" + try: + from services import pchome_mapping_backlog_service as backlog + from services.ai_exception_contract import ( + LEGACY_REVIEW_REQUIRED_COUNT_KEY, + PRIMARY_HUMAN_GATE_COUNT_KEY, + ) + + payload = _pchome_auto_policy_decision_preflight_contract_payload() + closeout = ( + backlog.build_pchome_auto_policy_db_apply_authorization_evidence_execution_closeout( + payload, + batch_size=max( + 1, min(_PCHOME_AUTO_POLICY_DECISION_PREFLIGHT_BATCH_SIZE, 50) + ), + execute_fetch=True, + timeout_seconds=max( + 1, + min(_PCHOME_AUTO_POLICY_DECISION_PREFLIGHT_TIMEOUT_SECONDS, 30), + ), + http_get=_pchome_auto_policy_machine_fetch_evidence_get, + ) + ) + summary = closeout.get("summary") or {} + final_gate = ( + closeout.get("future_database_apply_authorization_final_verifier_gate") + or {} + ) + evidence_closeout = ( + closeout.get("authorization_evidence_execution_closeout") or {} + ) + contract = ( + closeout.get("authorization_evidence_execution_closeout_contract") or {} + ) + safety = closeout.get("safety") or {} + closeout_fields = list( + evidence_closeout.get( + "authorization_evidence_execution_closeout_fields" + ) + or [] + ) + closeout_gates = list( + evidence_closeout.get( + "authorization_evidence_execution_closeout_acceptance_gates" + ) + or [] + ) + execution_fields = list( + evidence_closeout.get("authorization_evidence_execution_fields") or [] + ) + result = str(closeout.get("result") or "UNKNOWN") + + ready_count = int( + summary.get("authorization_evidence_execution_closeout_ready_count") or 0 + ) + check_count = int( + summary.get("authorization_evidence_execution_closeout_check_count") or 0 + ) + pass_count = int( + summary.get("authorization_evidence_execution_closeout_pass_count") or 0 + ) + waiting_count = int( + summary.get("authorization_evidence_execution_closeout_waiting_count") + or 0 + ) + preflight_ready_count = int( + summary.get("authorization_evidence_execution_preflight_ready_count") or 0 + ) + preflight_check_count = int( + summary.get("authorization_evidence_execution_preflight_check_count") or 0 + ) + closeout_ready_count = int( + summary.get("authorization_verifier_receipt_closeout_ready_count") or 0 + ) + verifier_closeout_check_count = int( + summary.get("verifier_receipt_closeout_check_count") or 0 + ) + detached_validation_ready_count = int( + summary.get( + "authorization_detached_verification_evidence_validation_ready_count" + ) + or 0 + ) + detached_validation_check_count = int( + summary.get("detached_verification_evidence_validation_check_count") or 0 + ) + verifier_handoff_count = int( + summary.get("verifier_receipt_evidence_handoff_count") or 0 + ) + execution_preflight_count = int( + summary.get("authorization_evidence_execution_preflight_count") or 0 + ) + evidence_closeout_count = int( + summary.get("authorization_evidence_execution_closeout_count") or 0 + ) + final_gate_count = int( + summary.get("database_apply_final_verifier_gate_count") or 0 + ) + final_gate_ready_count = int( + summary.get("database_apply_authorization_final_verifier_gate_ready_count") + or 0 + ) + closeout_field_count = int( + summary.get("authorization_evidence_execution_closeout_field_count") or 0 + ) + closeout_gate_count = int( + summary.get( + "authorization_evidence_execution_closeout_acceptance_gate_count" + ) + or 0 + ) + execution_field_count = int( + summary.get("authorization_evidence_execution_field_count") or 0 + ) + execution_gate_count = int( + summary.get("authorization_evidence_execution_acceptance_gate_count") or 0 + ) + verifier_field_count = int(summary.get("verifier_receipt_field_count") or 0) + verifier_acceptance_gate_count = int( + summary.get("verifier_receipt_acceptance_gate_count") or 0 + ) + handoff_field_count = int( + summary.get("verifier_receipt_evidence_handoff_field_count") or 0 + ) + handoff_acceptance_gate_count = int( + summary.get("verifier_receipt_handoff_acceptance_gate_count") or 0 + ) + required_evidence_count = int( + summary.get("required_external_receipt_evidence_count") or 0 + ) + external_gate_count = int( + summary.get("external_receipt_acceptance_gate_count") or 0 + ) + verifier_required_count = int( + summary.get("post_apply_verifier_required_count") or 0 + ) + same_run_truth_count = int(summary.get("same_run_truth_required_count") or 0) + writes_script_count = int(summary.get("writes_script_count") or 0) + writes_artifact_count = int(summary.get("writes_artifact_count") or 0) + reads_secret_count = int(summary.get("reads_secret_count") or 0) + executes_script_count = int(summary.get("executes_script_count") or 0) + executes_migration_count = int(summary.get("executes_migration_count") or 0) + executes_endpoint_count = int(summary.get("executes_endpoint_count") or 0) + executes_sql_count = int(summary.get("executes_sql_count") or 0) + writes_database_count = int(summary.get("writes_database_count") or 0) + signs_database_apply_authorization_count = int( + summary.get("signs_database_apply_authorization_count") or 0 + ) + primary_human_gate_count = int(summary.get(PRIMARY_HUMAN_GATE_COUNT_KEY) or 0) + manual_review_required_count = int( + summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0 + ) + side_effect_count = ( + writes_script_count + + writes_artifact_count + + reads_secret_count + + executes_script_count + + executes_migration_count + + executes_endpoint_count + + executes_sql_count + + writes_database_count + + signs_database_apply_authorization_count + ) + risk_detected = any( + [ + side_effect_count, + primary_human_gate_count, + manual_review_required_count, + safety.get("reads_secret_in_preview") is not False, + safety.get("writes_file") is not False, + safety.get("writes_script_in_preview") is not False, + safety.get("writes_artifact_in_preview") is not False, + safety.get("executes_script") is not False, + safety.get("executes_endpoint") is not False, + safety.get("executes_migration") is not False, + safety.get("executes_sql") is not False, + safety.get("writes_database") is not False, + safety.get("signs_database_apply_authorization") is not False, + safety.get("performs_detached_signature_verification") is not False, + safety.get("persists_verifier_receipt") is not False, + safety.get("executes_authorization_evidence") is not False, + safety.get("executes_database_apply") is not False, + final_gate.get("ready_for_database_apply_now") is not False, + final_gate.get("database_apply_authorized") is not False, + final_gate.get("issues_database_apply_authorization") is not False, + final_gate.get("signs_database_apply_authorization") is not False, + final_gate.get("executes_authorization_evidence") is not False, + final_gate.get("executes_database_apply") is not False, + final_gate.get("detached_signature_verification_performed") + is not False, + final_gate.get("verifier_receipt_persisted") is not False, + final_gate.get("external_signed_authorization_receipt_included") + is not False, + final_gate.get("signed_authorization_receipt_included") is not False, + final_gate.get("signature_material_included") is not False, + final_gate.get("secret_material_included") is not False, + final_gate.get("secret_material_required_in_preview") is not False, + final_gate.get("reads_secret_in_preview") is not False, + evidence_closeout.get("detached_signature_verification_performed") + is not False, + evidence_closeout.get("verifier_receipt_persisted") is not False, + evidence_closeout.get( + "external_signed_authorization_receipt_included" + ) + is not False, + evidence_closeout.get("signed_authorization_receipt_included") + is not False, + evidence_closeout.get("signature_material_included") is not False, + evidence_closeout.get("secret_material_included") is not False, + evidence_closeout.get("secret_material_required_in_preview") + is not False, + evidence_closeout.get("accepts_plaintext_secret") is not False, + evidence_closeout.get("reads_secret_in_preview") is not False, + evidence_closeout.get("executes_shell_in_preview") is not False, + evidence_closeout.get("executes_endpoint_in_preview") is not False, + evidence_closeout.get("executes_sql_in_preview") is not False, + evidence_closeout.get("writes_database_in_preview") is not False, + evidence_closeout.get("executes_authorization_evidence") is not False, + evidence_closeout.get("executes_database_apply") is not False, + evidence_closeout.get("ready_for_database_apply_now") is not False, + evidence_closeout.get("database_apply_authorized") is not False, + evidence_closeout.get("issues_database_apply_authorization") + is not False, + evidence_closeout.get("signs_database_apply_authorization") + is not False, + contract.get("accepts_plaintext_secret") is not False, + contract.get("performs_detached_signature_verification") is not False, + contract.get("persists_verifier_receipt") is not False, + contract.get("executes_authorization_evidence") is not False, + contract.get("executes_database_apply") is not False, + contract.get("issues_database_apply_authorization") is not False, + contract.get("database_apply_authorized") is not False, + contract.get("ready_for_database_apply_now") is not False, + contract.get("signs_database_apply_authorization") is not False, + contract.get("writes_database") is not False, + contract.get("executes_in_preview") is not False, + contract.get("secret_material_required_in_preview") is not False, + ] + ) + closeout_contract_present = ( + result == "DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT_READY" + and ready_count == 1 + and check_count >= 12 + and pass_count == check_count + and waiting_count == 0 + and preflight_ready_count == 1 + and preflight_check_count == 12 + and closeout_ready_count == 1 + and verifier_closeout_check_count == 12 + and detached_validation_ready_count == 1 + and detached_validation_check_count == 12 + and verifier_handoff_count == 1 + and execution_preflight_count == 1 + and evidence_closeout_count == 1 + and final_gate_count == 1 + and final_gate_ready_count == 1 + and closeout_field_count == 12 + and closeout_gate_count == 10 + and execution_field_count == 12 + and execution_gate_count == 10 + and verifier_field_count == 12 + and verifier_acceptance_gate_count == 10 + and handoff_field_count == 12 + and handoff_acceptance_gate_count == 10 + and required_evidence_count == 10 + and external_gate_count == 8 + and "final_verifier_gate_endpoint" in closeout_fields + and "no_database_apply_authorized_by_closeout" in closeout_gates + and "verifier_receipt_sha256" in execution_fields + and verifier_required_count == 1 + and same_run_truth_count == 1 + and final_gate.get( + "ready_for_future_database_apply_authorization_final_verifier_gate" + ) + is True + and final_gate.get( + "can_enter_future_database_apply_controlled_apply_final_preflight" + ) + is True + and final_gate.get("authorization_evidence_execution_closeout_ready") + is True + and final_gate.get("final_verifier_gate_ready") is True + and final_gate.get("ready_for_database_apply_now") is False + and final_gate.get("database_apply_authorized") is False + and evidence_closeout.get("authorization_material_type") + == "authorization_evidence_execution_closeout" + and evidence_closeout.get( + "ready_for_future_authorization_evidence_execution_closeout" + ) + is True + and evidence_closeout.get("requires_detached_signature_verification") + is True + and evidence_closeout.get("detached_signature_verification_performed") + is False + and evidence_closeout.get("verifier_receipt_persisted") is False + and evidence_closeout.get( + "external_signed_authorization_receipt_required_in_future" + ) + is True + and evidence_closeout.get("external_signed_authorization_receipt_included") + is False + and evidence_closeout.get("signed_authorization_receipt_included") + is False + and evidence_closeout.get("signature_material_included") is False + and evidence_closeout.get("signer_key_id_reference_only") is True + and evidence_closeout.get("signature_algorithm_reference_only") is True + and evidence_closeout.get("accepts_plaintext_secret") is False + and evidence_closeout.get("hash_matches") is True + and evidence_closeout.get("requires_post_apply_verifier") is True + and evidence_closeout.get( + "requires_fresh_production_truth_in_same_run" + ) + is True + and contract.get("machine_verifiable") is True + and contract.get( + "permits_future_database_apply_authorization_final_verifier_gate" + ) + is True + and contract.get( + "permits_future_database_apply_controlled_apply_final_preflight" + ) + is True + and contract.get("accepts_plaintext_secret") is False + and contract.get("performs_detached_signature_verification") is False + and contract.get("persists_verifier_receipt") is False + and contract.get("executes_authorization_evidence") is False + and contract.get("executes_database_apply") is False + and safety.get("manual_review_mode") == "exception_only" + ) + + if risk_detected: + status = "critical" + summary_text = ( + "PChome auto-policy authorization evidence execution closeout " + "偵測到 secret、signed material、verification execution、" + "verifier receipt persist、SQL、DB write、database apply、" + "簽發授權或人工主 gate 風險" + ) + next_machine_action = ( + "halt_pchome_auto_policy_authorization_evidence_execution_closeout_and_inspect_risk" + ) + elif closeout_contract_present: + status = "ok" + summary_text = ( + "PChome auto-policy authorization evidence execution closeout " + f"已自動完成 {pass_count}/{check_count} checks,final verifier gate 已綁定,apply/write/sign=0" + ) + next_machine_action = ( + "continue_to_pchome_auto_policy_database_apply_controlled_apply_final_preflight_lane" + ) + else: + status = "warning" + summary_text = ( + "PChome auto-policy authorization evidence execution closeout " + "尚未達自動監控契約" + ) + next_machine_action = ( + "refresh_pchome_auto_policy_authorization_evidence_execution_closeout" + ) + + return _check( + "PChome auto-policy authorization evidence execution closeout", + status, + summary_text, + { + "policy": closeout.get("policy"), + "result": result, + "source_policy": closeout.get("source_policy"), + "authorization_evidence_execution_closeout_ready_count": ready_count, + "authorization_evidence_execution_closeout_check_count": check_count, + "authorization_evidence_execution_closeout_pass_count": pass_count, + "authorization_evidence_execution_closeout_waiting_count": ( + waiting_count + ), + "authorization_evidence_execution_preflight_ready_count": ( + preflight_ready_count + ), + "authorization_evidence_execution_preflight_check_count": ( + preflight_check_count + ), + "authorization_verifier_receipt_closeout_ready_count": ( + closeout_ready_count + ), + "verifier_receipt_closeout_check_count": ( + verifier_closeout_check_count + ), + "detached_verification_evidence_validation_ready_count": ( + detached_validation_ready_count + ), + "detached_verification_evidence_validation_check_count": ( + detached_validation_check_count + ), + "verifier_receipt_evidence_handoff_count": verifier_handoff_count, + "authorization_evidence_execution_preflight_count": ( + execution_preflight_count + ), + "authorization_evidence_execution_closeout_count": ( + evidence_closeout_count + ), + "database_apply_final_verifier_gate_count": final_gate_count, + "database_apply_authorization_final_verifier_gate_ready_count": ( + final_gate_ready_count + ), + "authorization_evidence_execution_closeout_field_count": ( + closeout_field_count + ), + "authorization_evidence_execution_closeout_acceptance_gate_count": ( + closeout_gate_count + ), + "authorization_evidence_execution_field_count": execution_field_count, + "authorization_evidence_execution_acceptance_gate_count": ( + execution_gate_count + ), + "verifier_receipt_field_count": verifier_field_count, + "verifier_receipt_acceptance_gate_count": ( + verifier_acceptance_gate_count + ), + "verifier_receipt_evidence_handoff_field_count": handoff_field_count, + "verifier_receipt_handoff_acceptance_gate_count": ( + handoff_acceptance_gate_count + ), + "required_external_receipt_evidence_count": required_evidence_count, + "external_receipt_acceptance_gate_count": external_gate_count, + "post_apply_verifier_required_count": verifier_required_count, + "same_run_truth_required_count": same_run_truth_count, + "writes_script_count": writes_script_count, + "writes_artifact_count": writes_artifact_count, + "reads_secret_count": reads_secret_count, + "executes_script_count": executes_script_count, + "executes_migration_count": executes_migration_count, + "executes_endpoint_count": executes_endpoint_count, + "executes_sql_count": executes_sql_count, + "writes_database_count": writes_database_count, + "signs_database_apply_authorization_count": ( + signs_database_apply_authorization_count + ), + "primary_human_gate_count": primary_human_gate_count, + "manual_review_required_count": manual_review_required_count, + "manual_review_mode": safety.get("manual_review_mode"), + "payload_source": "local_contract_fixture", + "machine_fetch_evidence_source": "local_schema_fixture", + "http_get_adapter": "_pchome_auto_policy_machine_fetch_evidence_get", + "execute_fetch": True, + "outbound_network": False, + "business_data_source": False, + "ready_for_future_database_apply_authorization_final_verifier_gate": bool( + final_gate.get( + "ready_for_future_database_apply_authorization_final_verifier_gate" + ) + ), + "can_enter_future_database_apply_controlled_apply_final_preflight": bool( + final_gate.get( + "can_enter_future_database_apply_controlled_apply_final_preflight" + ) + ), + "authorization_evidence_execution_closeout_ready": bool( + final_gate.get("authorization_evidence_execution_closeout_ready") + ), + "ready_for_future_authorization_evidence_execution_closeout": bool( + evidence_closeout.get( + "ready_for_future_authorization_evidence_execution_closeout" + ) + ), + "permits_future_database_apply_authorization_final_verifier_gate": bool( + contract.get( + "permits_future_database_apply_authorization_final_verifier_gate" + ) + ), + "permits_future_database_apply_controlled_apply_final_preflight": bool( + contract.get( + "permits_future_database_apply_controlled_apply_final_preflight" + ) + ), + "requires_detached_signature_verification": bool( + evidence_closeout.get("requires_detached_signature_verification") + ), + "detached_signature_verification_performed": bool( + final_gate.get("detached_signature_verification_performed") + or evidence_closeout.get( + "detached_signature_verification_performed" + ) + or safety.get("performs_detached_signature_verification") + ), + "verifier_receipt_persisted": bool( + final_gate.get("verifier_receipt_persisted") + or evidence_closeout.get("verifier_receipt_persisted") + or safety.get("persists_verifier_receipt") + ), + "external_signed_authorization_receipt_required_in_future": bool( + evidence_closeout.get( + "external_signed_authorization_receipt_required_in_future" + ) + ), + "external_signed_authorization_receipt_included": bool( + final_gate.get("external_signed_authorization_receipt_included") + or evidence_closeout.get( + "external_signed_authorization_receipt_included" + ) + ), + "signed_authorization_receipt_included": bool( + final_gate.get("signed_authorization_receipt_included") + or evidence_closeout.get("signed_authorization_receipt_included") + ), + "signature_material_included": bool( + final_gate.get("signature_material_included") + or evidence_closeout.get("signature_material_included") + ), + "signer_key_id_reference_only": bool( + evidence_closeout.get("signer_key_id_reference_only") + ), + "signature_algorithm_reference_only": bool( + evidence_closeout.get("signature_algorithm_reference_only") + ), + "secret_material_included": bool( + final_gate.get("secret_material_included") + or evidence_closeout.get("secret_material_included") + ), + "secret_material_required_in_preview": bool( + final_gate.get("secret_material_required_in_preview") + or evidence_closeout.get("secret_material_required_in_preview") + or contract.get("secret_material_required_in_preview") + ), + "accepts_plaintext_secret": bool( + evidence_closeout.get("accepts_plaintext_secret") + or contract.get("accepts_plaintext_secret") + ), + "reads_secret_in_preview": bool( + final_gate.get("reads_secret_in_preview") + or evidence_closeout.get("reads_secret_in_preview") + or safety.get("reads_secret_in_preview") + ), + "executes_authorization_evidence": bool( + final_gate.get("executes_authorization_evidence") + or evidence_closeout.get("executes_authorization_evidence") + or contract.get("executes_authorization_evidence") + or safety.get("executes_authorization_evidence") + ), + "executes_database_apply": bool( + final_gate.get("executes_database_apply") + or evidence_closeout.get("executes_database_apply") + or contract.get("executes_database_apply") + or safety.get("executes_database_apply") + ), + "ready_for_database_apply_now": bool( + final_gate.get("ready_for_database_apply_now") + or evidence_closeout.get("ready_for_database_apply_now") + or contract.get("ready_for_database_apply_now") + ), + "database_apply_authorized": bool( + final_gate.get("database_apply_authorized") + or evidence_closeout.get("database_apply_authorized") + or contract.get("database_apply_authorized") + ), + "issues_database_apply_authorization": bool( + final_gate.get("issues_database_apply_authorization") + or evidence_closeout.get("issues_database_apply_authorization") + or contract.get("issues_database_apply_authorization") + ), + "signs_database_apply_authorization": bool( + final_gate.get("signs_database_apply_authorization") + or evidence_closeout.get("signs_database_apply_authorization") + or contract.get("signs_database_apply_authorization") + ), + "requires_post_apply_verifier": bool( + evidence_closeout.get("requires_post_apply_verifier") + ), + "requires_fresh_production_truth": bool( + evidence_closeout.get( + "requires_fresh_production_truth_in_same_run" + ) + ), + "writes_database": False, + "next_machine_action": next_machine_action, + }, + ) + except Exception as exc: + return _check( + "PChome auto-policy authorization evidence execution closeout", + "warning", + ( + "PChome auto-policy authorization evidence execution closeout " + f"例行監控暫時無法讀取:{exc}" + ), + { + "writes_database": False, + "writes_database_count": 0, + "signs_database_apply_authorization_count": 0, + "primary_human_gate_count": 0, + "execute_fetch": True, + "outbound_network": False, + "business_data_source": False, + "payload_source": "local_contract_fixture", + "next_machine_action": ( + "refresh_pchome_auto_policy_authorization_evidence_execution_closeout" + ), + }, + ) + + def _ai_surface_html_readback_check() -> Dict[str, Any]: """Read-only AI product surface guardrail readback.""" try: @@ -9244,6 +10227,7 @@ def collect_ai_automation_smoke(*, record_history: bool = True, history_limit: i _pchome_auto_policy_detached_verification_evidence_validation_check(), _pchome_auto_policy_verifier_receipt_closeout_check(), _pchome_auto_policy_authorization_evidence_execution_preflight_check(), + _pchome_auto_policy_authorization_evidence_execution_closeout_check(), _ai_surface_html_readback_check(), _sitewide_ui_ux_agent_check(), _sitewide_visual_qa_check(), diff --git a/tests/test_ai_automation_metrics.py b/tests/test_ai_automation_metrics.py index e9f889c..e3b2156 100644 --- a/tests/test_ai_automation_metrics.py +++ b/tests/test_ai_automation_metrics.py @@ -107,10 +107,10 @@ def test_system_metrics_exports_scheduled_health_summary(): Gauge, { "summary": { - "ok": 20, + "ok": 21, "warning": 1, "critical": 0, - "total": 21, + "total": 22, "primary_human_gate_count": 0, "writes_database_count": 0, }, @@ -133,6 +133,7 @@ def test_system_metrics_exports_scheduled_health_summary(): {"key": "pchome_auto_policy_detached_verification_evidence_validation", "status": "ok"}, {"key": "pchome_auto_policy_verifier_receipt_closeout", "status": "ok"}, {"key": "pchome_auto_policy_authorization_evidence_execution_preflight", "status": "ok"}, + {"key": "pchome_auto_policy_authorization_evidence_execution_closeout", "status": "ok"}, {"key": "sitewide_ui_ux_agent", "status": "ok"}, {"key": "sitewide_visual_qa", "status": "ok"}, ], @@ -140,7 +141,7 @@ def test_system_metrics_exports_scheduled_health_summary(): ) output = generate_latest(registry).decode("utf-8") - assert 'momo_ai_automation_scheduled_health_summary_total{status="ok"} 20.0' in output + assert 'momo_ai_automation_scheduled_health_summary_total{status="ok"} 21.0' in output assert 'momo_ai_automation_scheduled_health_summary_total{status="warning"} 1.0' in output assert ( 'momo_ai_automation_scheduled_health_family_status{family="ai_automation_smoke",status="ok"} 1.0' @@ -214,6 +215,10 @@ def test_system_metrics_exports_scheduled_health_summary(): 'momo_ai_automation_scheduled_health_family_status{family="pchome_auto_policy_authorization_evidence_execution_preflight",status="ok"} 1.0' in output ) + assert ( + 'momo_ai_automation_scheduled_health_family_status{family="pchome_auto_policy_authorization_evidence_execution_closeout",status="ok"} 1.0' + in output + ) assert ( 'momo_ai_automation_scheduled_health_family_status{family="sitewide_ui_ux_agent",status="ok"} 1.0' in output diff --git a/tests/test_ai_automation_smoke_service.py b/tests/test_ai_automation_smoke_service.py index 8e0772c..4e9ca74 100644 --- a/tests/test_ai_automation_smoke_service.py +++ b/tests/test_ai_automation_smoke_service.py @@ -850,6 +850,94 @@ def _auto_policy_authorization_evidence_execution_preflight_history_check(status } +def _auto_policy_authorization_evidence_execution_closeout_history_check(status="ok"): + return { + "name": "PChome auto-policy authorization evidence execution closeout", + "status": status, + "summary": ( + "PChome auto-policy authorization evidence execution closeout 已自動完成" + ), + "details": { + "policy": ( + "read_only_pchome_growth_auto_policy_db_apply_authorization_" + "evidence_execution_closeout" + ), + "result": "DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT_READY", + "authorization_evidence_execution_closeout_ready_count": 1, + "authorization_evidence_execution_closeout_check_count": 12, + "authorization_evidence_execution_closeout_pass_count": 12, + "authorization_evidence_execution_closeout_waiting_count": 0, + "authorization_evidence_execution_preflight_ready_count": 1, + "authorization_evidence_execution_preflight_check_count": 12, + "authorization_verifier_receipt_closeout_ready_count": 1, + "verifier_receipt_closeout_check_count": 12, + "detached_verification_evidence_validation_ready_count": 1, + "detached_verification_evidence_validation_check_count": 12, + "verifier_receipt_evidence_handoff_count": 1, + "authorization_evidence_execution_preflight_count": 1, + "authorization_evidence_execution_closeout_count": 1, + "database_apply_final_verifier_gate_count": 1, + "database_apply_authorization_final_verifier_gate_ready_count": 1, + "authorization_evidence_execution_closeout_field_count": 12, + "authorization_evidence_execution_closeout_acceptance_gate_count": 10, + "authorization_evidence_execution_field_count": 12, + "authorization_evidence_execution_acceptance_gate_count": 10, + "verifier_receipt_field_count": 12, + "verifier_receipt_acceptance_gate_count": 10, + "verifier_receipt_evidence_handoff_field_count": 12, + "verifier_receipt_handoff_acceptance_gate_count": 10, + "required_external_receipt_evidence_count": 10, + "external_receipt_acceptance_gate_count": 8, + "post_apply_verifier_required_count": 1, + "same_run_truth_required_count": 1, + "writes_script_count": 0, + "writes_artifact_count": 0, + "reads_secret_count": 0, + "executes_script_count": 0, + "executes_migration_count": 0, + "executes_endpoint_count": 0, + "executes_sql_count": 0, + "writes_database_count": 0, + "signs_database_apply_authorization_count": 0, + "primary_human_gate_count": 0, + "manual_review_required_count": 0, + "manual_review_mode": "exception_only", + "payload_source": "local_contract_fixture", + "execute_fetch": True, + "outbound_network": False, + "business_data_source": False, + "ready_for_future_database_apply_authorization_final_verifier_gate": True, + "can_enter_future_database_apply_controlled_apply_final_preflight": True, + "authorization_evidence_execution_closeout_ready": True, + "ready_for_future_authorization_evidence_execution_closeout": True, + "permits_future_database_apply_authorization_final_verifier_gate": True, + "permits_future_database_apply_controlled_apply_final_preflight": True, + "requires_detached_signature_verification": True, + "detached_signature_verification_performed": False, + "verifier_receipt_persisted": False, + "external_signed_authorization_receipt_required_in_future": True, + "external_signed_authorization_receipt_included": False, + "signed_authorization_receipt_included": False, + "signature_material_included": False, + "signer_key_id_reference_only": True, + "signature_algorithm_reference_only": True, + "secret_material_included": False, + "secret_material_required_in_preview": False, + "accepts_plaintext_secret": False, + "reads_secret_in_preview": False, + "executes_authorization_evidence": False, + "executes_database_apply": False, + "ready_for_database_apply_now": False, + "database_apply_authorized": False, + "issues_database_apply_authorization": False, + "signs_database_apply_authorization": False, + "next_machine_action": ( + "continue_to_pchome_auto_policy_database_apply_controlled_apply_final_preflight_lane" + ), + }, + } + + def test_event_router_smoke_reports_queued_deliveries(tmp_path, monkeypatch): from services import ai_automation_metrics as metrics from services import ai_automation_smoke_service as smoke @@ -901,6 +989,7 @@ def test_collect_ai_automation_smoke_uses_worst_status(monkeypatch): monkeypatch.setattr(smoke, "_pchome_auto_policy_detached_verification_evidence_validation_check", lambda: smoke._check("auto-policy detached verification evidence validation", "ok", "ok")) monkeypatch.setattr(smoke, "_pchome_auto_policy_verifier_receipt_closeout_check", lambda: smoke._check("auto-policy verifier receipt closeout", "ok", "ok")) monkeypatch.setattr(smoke, "_pchome_auto_policy_authorization_evidence_execution_preflight_check", lambda: smoke._check("auto-policy authorization evidence execution preflight", "ok", "ok")) + monkeypatch.setattr(smoke, "_pchome_auto_policy_authorization_evidence_execution_closeout_check", lambda: smoke._check("auto-policy authorization evidence execution closeout", "ok", "ok")) monkeypatch.setattr(smoke, "_ai_surface_html_readback_check", lambda: smoke._check("surface", "ok", "ok")) monkeypatch.setattr(smoke, "_sitewide_ui_ux_agent_check", lambda: smoke._check("sitewide", "ok", "ok")) monkeypatch.setattr(smoke, "_sitewide_visual_qa_check", lambda: smoke._check("visual", "ok", "ok")) @@ -908,7 +997,7 @@ def test_collect_ai_automation_smoke_uses_worst_status(monkeypatch): result = smoke.collect_ai_automation_smoke(record_history=False) assert result["status"] == "critical" - assert result["summary"] == {"ok": 24, "warning": 1, "critical": 1, "total": 26} + assert result["summary"] == {"ok": 25, "warning": 1, "critical": 1, "total": 27} def test_pchome_controlled_apply_drift_monitor_reports_verified_zero_drift(monkeypatch): @@ -2939,6 +3028,141 @@ def test_pchome_auto_policy_authorization_evidence_execution_preflight_reports_h ) +def test_pchome_auto_policy_authorization_evidence_execution_closeout_reports_final_gate_no_apply(): + from services import ai_automation_smoke_service as smoke + + result = ( + smoke._pchome_auto_policy_authorization_evidence_execution_closeout_check() + ) + + assert result["status"] == "ok" + assert result["details"]["result"] == ( + "DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT_READY" + ) + assert ( + result["details"][ + "authorization_evidence_execution_closeout_pass_count" + ] + == 12 + ) + assert ( + result["details"][ + "authorization_evidence_execution_closeout_check_count" + ] + == 12 + ) + assert ( + result["details"][ + "authorization_evidence_execution_closeout_waiting_count" + ] + == 0 + ) + assert result["details"]["authorization_evidence_execution_preflight_ready_count"] == 1 + assert result["details"]["authorization_evidence_execution_preflight_check_count"] == 12 + assert result["details"]["authorization_verifier_receipt_closeout_ready_count"] == 1 + assert result["details"]["verifier_receipt_closeout_check_count"] == 12 + assert ( + result["details"][ + "detached_verification_evidence_validation_check_count" + ] + == 12 + ) + assert result["details"]["verifier_receipt_evidence_handoff_count"] == 1 + assert result["details"]["authorization_evidence_execution_preflight_count"] == 1 + assert result["details"]["authorization_evidence_execution_closeout_count"] == 1 + assert result["details"]["database_apply_final_verifier_gate_count"] == 1 + assert ( + result["details"][ + "database_apply_authorization_final_verifier_gate_ready_count" + ] + == 1 + ) + assert result["details"]["authorization_evidence_execution_closeout_field_count"] == 12 + assert ( + result["details"][ + "authorization_evidence_execution_closeout_acceptance_gate_count" + ] + == 10 + ) + assert result["details"]["authorization_evidence_execution_field_count"] == 12 + assert ( + result["details"]["authorization_evidence_execution_acceptance_gate_count"] + == 10 + ) + assert result["details"]["verifier_receipt_field_count"] == 12 + assert result["details"]["verifier_receipt_acceptance_gate_count"] == 10 + assert result["details"]["verifier_receipt_evidence_handoff_field_count"] == 12 + assert result["details"]["verifier_receipt_handoff_acceptance_gate_count"] == 10 + assert result["details"]["required_external_receipt_evidence_count"] == 10 + assert result["details"]["external_receipt_acceptance_gate_count"] == 8 + assert result["details"]["post_apply_verifier_required_count"] == 1 + assert result["details"]["same_run_truth_required_count"] == 1 + assert result["details"]["payload_source"] == "local_contract_fixture" + assert result["details"]["machine_fetch_evidence_source"] == "local_schema_fixture" + assert result["details"]["outbound_network"] is False + assert result["details"]["business_data_source"] is False + assert result["details"]["primary_human_gate_count"] == 0 + assert result["details"]["manual_review_required_count"] == 0 + assert result["details"]["reads_secret_count"] == 0 + assert result["details"]["executes_script_count"] == 0 + assert result["details"]["executes_sql_count"] == 0 + assert result["details"]["writes_database_count"] == 0 + assert result["details"]["signs_database_apply_authorization_count"] == 0 + assert ( + result["details"][ + "ready_for_future_database_apply_authorization_final_verifier_gate" + ] + is True + ) + assert ( + result["details"][ + "can_enter_future_database_apply_controlled_apply_final_preflight" + ] + is True + ) + assert ( + result["details"]["authorization_evidence_execution_closeout_ready"] + is True + ) + assert ( + result["details"][ + "ready_for_future_authorization_evidence_execution_closeout" + ] + is True + ) + assert ( + result["details"][ + "permits_future_database_apply_authorization_final_verifier_gate" + ] + is True + ) + assert ( + result["details"][ + "permits_future_database_apply_controlled_apply_final_preflight" + ] + is True + ) + assert result["details"]["requires_detached_signature_verification"] is True + assert result["details"]["detached_signature_verification_performed"] is False + assert result["details"]["verifier_receipt_persisted"] is False + assert result["details"]["external_signed_authorization_receipt_included"] is False + assert result["details"]["signed_authorization_receipt_included"] is False + assert result["details"]["signature_material_included"] is False + assert result["details"]["secret_material_included"] is False + assert result["details"]["secret_material_required_in_preview"] is False + assert result["details"]["accepts_plaintext_secret"] is False + assert result["details"]["reads_secret_in_preview"] is False + assert result["details"]["executes_authorization_evidence"] is False + assert result["details"]["executes_database_apply"] is False + assert result["details"]["ready_for_database_apply_now"] is False + assert result["details"]["database_apply_authorized"] is False + assert result["details"]["issues_database_apply_authorization"] is False + assert result["details"]["signs_database_apply_authorization"] is False + assert result["details"]["next_machine_action"] == ( + "continue_to_pchome_auto_policy_database_apply_controlled_apply_final_preflight_lane" + ) + + def test_collect_ai_automation_smoke_persists_recent_history(tmp_path, monkeypatch): from services import ai_automation_smoke_service as smoke @@ -2968,6 +3192,7 @@ def test_collect_ai_automation_smoke_persists_recent_history(tmp_path, monkeypat monkeypatch.setattr(smoke, "_pchome_auto_policy_detached_verification_evidence_validation_check", lambda: smoke._check("auto-policy detached verification evidence validation", "ok", "ok")) monkeypatch.setattr(smoke, "_pchome_auto_policy_verifier_receipt_closeout_check", lambda: smoke._check("auto-policy verifier receipt closeout", "ok", "ok")) monkeypatch.setattr(smoke, "_pchome_auto_policy_authorization_evidence_execution_preflight_check", lambda: smoke._check("auto-policy authorization evidence execution preflight", "ok", "ok")) + monkeypatch.setattr(smoke, "_pchome_auto_policy_authorization_evidence_execution_closeout_check", lambda: smoke._check("auto-policy authorization evidence execution closeout", "ok", "ok")) monkeypatch.setattr(smoke, "_ai_surface_html_readback_check", lambda: smoke._check("surface", "ok", "ok")) monkeypatch.setattr(smoke, "_sitewide_ui_ux_agent_check", lambda: smoke._check("sitewide", "ok", "ok")) monkeypatch.setattr(smoke, "_sitewide_visual_qa_check", lambda: smoke._check("visual", "ok", "ok")) @@ -3026,7 +3251,7 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects( json.dumps({ "generated_at": datetime.now().isoformat(timespec="seconds"), "status": "ok", - "summary": {"ok": 26, "warning": 0, "critical": 0, "total": 26}, + "summary": {"ok": 27, "warning": 0, "critical": 0, "total": 27}, "checks": [ { "name": "PChome 受控落地 drift monitor", @@ -3056,6 +3281,7 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects( _auto_policy_detached_verification_evidence_validation_history_check(), _auto_policy_verifier_receipt_closeout_history_check(), _auto_policy_authorization_evidence_execution_preflight_history_check(), + _auto_policy_authorization_evidence_execution_closeout_history_check(), { "name": "AI surface HTML readback", "status": "ok", @@ -3117,7 +3343,7 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects( ) assert summary["policy"] == "read_only_ai_automation_scheduled_health_summary" assert summary["status"] == "ok" - assert summary["summary"]["total"] == 23 + assert summary["summary"]["total"] == 24 assert summary["summary"]["primary_human_gate_count"] == 0 assert summary["summary"]["writes_database_count"] == 0 assert pchome_family["status"] == "ok" @@ -4359,6 +4585,158 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects( ] is False ) + authorization_evidence_closeout_family = next( + item for item in summary["families"] + if ( + item["key"] + == "pchome_auto_policy_authorization_evidence_execution_closeout" + ) + ) + assert authorization_evidence_closeout_family["status"] == "ok" + assert ( + authorization_evidence_closeout_family["details"][ + "authorization_evidence_execution_closeout_pass_count" + ] + == 12 + ) + assert ( + authorization_evidence_closeout_family["details"][ + "authorization_evidence_execution_preflight_ready_count" + ] + == 1 + ) + assert ( + authorization_evidence_closeout_family["details"][ + "database_apply_authorization_final_verifier_gate_ready_count" + ] + == 1 + ) + assert ( + authorization_evidence_closeout_family["details"][ + "authorization_evidence_execution_closeout_field_count" + ] + == 12 + ) + assert ( + authorization_evidence_closeout_family["details"][ + "authorization_evidence_execution_closeout_acceptance_gate_count" + ] + == 10 + ) + assert ( + authorization_evidence_closeout_family["details"][ + "verifier_receipt_evidence_handoff_field_count" + ] + == 12 + ) + assert ( + authorization_evidence_closeout_family["details"][ + "required_external_receipt_evidence_count" + ] + == 10 + ) + assert ( + authorization_evidence_closeout_family["details"]["payload_source"] + == "local_contract_fixture" + ) + assert ( + authorization_evidence_closeout_family["details"]["outbound_network"] + is False + ) + assert ( + authorization_evidence_closeout_family["details"]["business_data_source"] + is False + ) + assert ( + authorization_evidence_closeout_family["details"]["primary_human_gate_count"] + == 0 + ) + assert ( + authorization_evidence_closeout_family["details"]["reads_secret_count"] + == 0 + ) + assert ( + authorization_evidence_closeout_family["details"]["writes_database_count"] + == 0 + ) + assert ( + authorization_evidence_closeout_family["details"][ + "signs_database_apply_authorization_count" + ] + == 0 + ) + assert ( + authorization_evidence_closeout_family["details"][ + "ready_for_future_database_apply_authorization_final_verifier_gate" + ] + is True + ) + assert ( + authorization_evidence_closeout_family["details"][ + "can_enter_future_database_apply_controlled_apply_final_preflight" + ] + is True + ) + assert ( + authorization_evidence_closeout_family["details"][ + "permits_future_database_apply_authorization_final_verifier_gate" + ] + is True + ) + assert ( + authorization_evidence_closeout_family["details"][ + "permits_future_database_apply_controlled_apply_final_preflight" + ] + is True + ) + assert ( + authorization_evidence_closeout_family["details"][ + "detached_signature_verification_performed" + ] + is False + ) + assert ( + authorization_evidence_closeout_family["details"]["verifier_receipt_persisted"] + is False + ) + assert ( + authorization_evidence_closeout_family["details"][ + "external_signed_authorization_receipt_included" + ] + is False + ) + assert ( + authorization_evidence_closeout_family["details"][ + "signed_authorization_receipt_included" + ] + is False + ) + assert ( + authorization_evidence_closeout_family["details"]["signature_material_included"] + is False + ) + assert ( + authorization_evidence_closeout_family["details"]["secret_material_included"] + is False + ) + assert ( + authorization_evidence_closeout_family["details"]["accepts_plaintext_secret"] + is False + ) + assert ( + authorization_evidence_closeout_family["details"][ + "executes_authorization_evidence" + ] + is False + ) + assert ( + authorization_evidence_closeout_family["details"]["executes_database_apply"] + is False + ) + assert ( + authorization_evidence_closeout_family["details"]["database_apply_authorized"] + is False + ) surface_family = next( item for item in summary["families"] if item["key"] == "ai_surface_html_readback" @@ -4393,7 +4771,7 @@ def test_scheduled_automation_health_summary_can_use_current_smoke_without_recor current_smoke = { "generated_at": "2026-07-02T12:00:00", "status": "critical", - "summary": {"ok": 25, "warning": 0, "critical": 1, "total": 26}, + "summary": {"ok": 26, "warning": 0, "critical": 1, "total": 27}, "checks": [ { "name": "PChome 受控落地 drift monitor", @@ -4417,6 +4795,7 @@ def test_scheduled_automation_health_summary_can_use_current_smoke_without_recor _auto_policy_detached_verification_evidence_validation_history_check(), _auto_policy_verifier_receipt_closeout_history_check(), _auto_policy_authorization_evidence_execution_preflight_history_check(), + _auto_policy_authorization_evidence_execution_closeout_history_check(), { "name": "AI surface HTML readback", "status": "ok", @@ -4472,7 +4851,7 @@ def test_scheduled_automation_health_summary_falls_back_to_visual_qa_artifact(tm json.dumps({ "generated_at": datetime.now().isoformat(timespec="seconds"), "status": "ok", - "summary": {"ok": 24, "warning": 0, "critical": 0, "total": 24}, + "summary": {"ok": 25, "warning": 0, "critical": 0, "total": 25}, "checks": [], }, ensure_ascii=False) + "\n", encoding="utf-8", @@ -4558,6 +4937,11 @@ def test_scheduled_automation_health_summary_falls_back_to_visual_qa_artifact(tm "_pchome_auto_policy_authorization_evidence_execution_preflight_check", lambda: _auto_policy_authorization_evidence_execution_preflight_history_check(), ) + monkeypatch.setattr( + smoke, + "_pchome_auto_policy_authorization_evidence_execution_closeout_check", + lambda: _auto_policy_authorization_evidence_execution_closeout_history_check(), + ) monkeypatch.setattr( surface_service, "build_sitewide_visual_qa_readback", @@ -4606,7 +4990,7 @@ def test_scheduled_automation_health_summary_route_returns_compact_payload(tmp_p json.dumps({ "generated_at": datetime.now().isoformat(timespec="seconds"), "status": "ok", - "summary": {"ok": 26, "warning": 0, "critical": 0, "total": 26}, + "summary": {"ok": 27, "warning": 0, "critical": 0, "total": 27}, "checks": [], }, ensure_ascii=False) + "\n", encoding="utf-8", @@ -4692,6 +5076,11 @@ def test_scheduled_automation_health_summary_route_returns_compact_payload(tmp_p "_pchome_auto_policy_authorization_evidence_execution_preflight_check", lambda: _auto_policy_authorization_evidence_execution_preflight_history_check(), ) + monkeypatch.setattr( + smoke, + "_pchome_auto_policy_authorization_evidence_execution_closeout_check", + lambda: _auto_policy_authorization_evidence_execution_closeout_history_check(), + ) app = Flask(__name__) with app.test_request_context( @@ -4931,6 +5320,7 @@ def test_surface_html_readback_check_is_part_of_ai_smoke(monkeypatch): monkeypatch.setattr(smoke, "_pchome_auto_policy_detached_verification_evidence_validation_check", lambda: smoke._check("auto-policy detached verification evidence validation", "ok", "ok")) monkeypatch.setattr(smoke, "_pchome_auto_policy_verifier_receipt_closeout_check", lambda: smoke._check("auto-policy verifier receipt closeout", "ok", "ok")) monkeypatch.setattr(smoke, "_pchome_auto_policy_authorization_evidence_execution_preflight_check", lambda: smoke._check("auto-policy authorization evidence execution preflight", "ok", "ok")) + monkeypatch.setattr(smoke, "_pchome_auto_policy_authorization_evidence_execution_closeout_check", lambda: smoke._check("auto-policy authorization evidence execution closeout", "ok", "ok")) monkeypatch.setattr(smoke, "_sitewide_visual_qa_check", lambda: smoke._check( "Sitewide visual QA readback", "ok", @@ -4952,7 +5342,7 @@ def test_surface_html_readback_check_is_part_of_ai_smoke(monkeypatch): item for item in result["checks"] if item["name"] == "Sitewide visual QA readback" ) - assert result["summary"]["total"] == 26 + assert result["summary"]["total"] == 27 assert surface_check["status"] == "ok" assert surface_check["details"]["checked_surface_count"] == 10 assert sitewide_check["status"] == "ok"