feat(smoke): monitor pchome signing execution preflight
Some checks failed
CD Pipeline / deploy (push) Has been cancelled

This commit is contained in:
ogt
2026-07-03 10:00:38 +08:00
parent 6ee4244079
commit 2326227fed
6 changed files with 874 additions and 18 deletions

View File

@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.733"
SYSTEM_VERSION = "V10.734"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -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 monitoringsigning issuer closeout monitoring 已建立GCP embedding 熔斷延後處理、110 proxy rescue 與 direct host health skip 已建立
> **適用版本**: V10.733
> **狀態**: 🟢 四 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 monitoringsigning issuer closeout monitoring 與 signing execution preflight monitoring 已建立GCP embedding 熔斷延後處理、110 proxy rescue 與 direct host health skip 已建立
> **適用版本**: V10.734
---
@@ -249,6 +249,7 @@ SQL漏斗(~300筆)
- `/api/ai-automation/smoke` 必須包含 `PChome auto-policy signing decision closeout`:以本機 deterministic contract fixture 與 machine evidence adapter 推進 no-write signing decision closeout ready path並明確回報 `signing_decision_closeout_check_count=12``signing_decision_input_requirement_count=10``signing_decision_rejection_reason_count=11``payload_source=local_contract_fixture``outbound_network=false``business_data_source=false``writes_database_count=0``signs_database_apply_authorization_count=0``primary_human_gate_count=0``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`。此 check 不讀 secret、不執行 shell/SQL、不寫 DB、不簽發 apply authorization`/api/ai-automation/scheduled-health-summary` 需輸出 `pchome_auto_policy_signing_decision_closeout` family`/metrics` 需暴露對應 `momo_ai_automation_scheduled_health_family_status`
- `/api/ai-automation/smoke` 必須包含 `PChome auto-policy signing issuer guard`:以本機 deterministic contract fixture 與 machine evidence adapter 推進 no-write signing issuer guard ready path並明確回報 `signing_issuer_guard_check_count=12``signing_decision_closeout_check_count=12``signing_decision_input_requirement_count=10``signing_decision_rejection_reason_count=11``payload_source=local_contract_fixture``outbound_network=false``business_data_source=false``writes_database_count=0``signs_database_apply_authorization_count=0``primary_human_gate_count=0``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``ready_for_future_signable_request_boundary=true`。此 check 不讀 secret、不執行 shell/SQL、不寫 DB、不簽發 apply authorization`/api/ai-automation/scheduled-health-summary` 需輸出 `pchome_auto_policy_signing_issuer_guard` family`/metrics` 需暴露對應 `momo_ai_automation_scheduled_health_family_status`
- `/api/ai-automation/smoke` 必須包含 `PChome auto-policy signing issuer closeout`:以本機 deterministic contract fixture 與 machine evidence adapter 推進 no-write final signable request package ready path並明確回報 `signing_issuer_closeout_check_count=12``signing_issuer_guard_check_count=12``signing_decision_input_requirement_count=10``signing_decision_rejection_reason_count=11``payload_source=local_contract_fixture``outbound_network=false``business_data_source=false``writes_database_count=0``signs_database_apply_authorization_count=0``primary_human_gate_count=0``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``ready_for_future_final_signable_request_package=true`。此 check 不讀 secret、不執行 shell/SQL、不寫 DB、不簽發 apply authorization`/api/ai-automation/scheduled-health-summary` 需輸出 `pchome_auto_policy_signing_issuer_closeout` family`/metrics` 需暴露對應 `momo_ai_automation_scheduled_health_family_status`
- `/api/ai-automation/smoke` 必須包含 `PChome auto-policy signing execution preflight`:以本機 deterministic contract fixture 與 machine evidence adapter 推進 no-write operator-held secret boundary preflight path並明確回報 `signing_execution_preflight_check_count=12``signing_issuer_closeout_check_count=12``operator_held_secret_boundary_count=1``signing_execution_input_requirement_count=10``signing_execution_abort_condition_count=8``rollback_boundary_count=4``payload_source=local_contract_fixture``outbound_network=false``business_data_source=false``reads_secret_count=0``writes_database_count=0``signs_database_apply_authorization_count=0``primary_human_gate_count=0``secret_reference_mode=external_runtime_reference_only``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``ready_for_future_signing_execution_preflight=true`。此 check 不讀 secret、不執行 shell/SQL、不寫 DB、不簽發 apply authorization`/api/ai-automation/scheduled-health-summary` 需輸出 `pchome_auto_policy_signing_execution_preflight` family`/metrics` 需暴露對應 `momo_ai_automation_scheduled_health_family_status`
- 健康檢查 API 會將最近檢查結果保存到 JSONL頁面顯示最近狀態趨勢。
- 健康檢查歷史支援 JSONL 匯出、清理與每日「正常 / 注意 / 嚴重」摘要。
- 健康檢查每日摘要支援手動 Telegram 推播,並由 `momo-scheduler` 每日 09:10 呼叫 `run_ai_smoke_daily_summary_task()`
@@ -858,6 +859,7 @@ POSTGRES_HOST=momo-db
| 2026-07-03 | PChome auto-policy signing decision closeout 必須有 runtime monitoring | V10.731 起 `/api/ai-automation/smoke``/api/ai-automation/scheduled-health-summary``/metrics` 必須輸出 `PChome auto-policy signing decision closeout` / `pchome_auto_policy_signing_decision_closeout`;此 runtime check 自動驗證 signing decision closeout ready path、unsigned signing decision package、post-apply verifier requirement 與 same-run production truth requirement但明確標記 `writes_database_count=0``signs_database_apply_authorization_count=0``primary_human_gate_count=0``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、不執行 SQL、不寫 DB、不簽發 apply authorization。 |
| 2026-07-03 | PChome auto-policy signing issuer guard 必須有 runtime monitoring | V10.732 起 `/api/ai-automation/smoke``/api/ai-automation/scheduled-health-summary``/metrics` 必須輸出 `PChome auto-policy signing issuer guard` / `pchome_auto_policy_signing_issuer_guard`;此 runtime check 自動驗證 signing issuer guard ready path、future signable request boundary、post-apply verifier requirement 與 same-run production truth requirement但明確標記 `writes_database_count=0``signs_database_apply_authorization_count=0``primary_human_gate_count=0``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、不執行 SQL、不寫 DB、不簽發 apply authorization。 |
| 2026-07-03 | PChome auto-policy signing issuer closeout 必須有 runtime monitoring | V10.733 起 `/api/ai-automation/smoke``/api/ai-automation/scheduled-health-summary``/metrics` 必須輸出 `PChome auto-policy signing issuer closeout` / `pchome_auto_policy_signing_issuer_closeout`;此 runtime check 自動驗證 final signable request package、signing issuer guard source chain、post-apply verifier requirement 與 same-run production truth requirement但明確標記 `writes_database_count=0``signs_database_apply_authorization_count=0``primary_human_gate_count=0``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、不執行 SQL、不寫 DB、不簽發 apply authorization。 |
| 2026-07-03 | PChome auto-policy signing execution preflight 必須有 runtime monitoring | V10.734 起 `/api/ai-automation/smoke``/api/ai-automation/scheduled-health-summary``/metrics` 必須輸出 `PChome auto-policy signing execution preflight` / `pchome_auto_policy_signing_execution_preflight`;此 runtime check 自動驗證 operator-held secret boundary、nonsecret signing inputs、future command preview、rollback boundary、abort conditions、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``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、不執行 SQL、不寫 DB、不簽發 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 已授權。 |

View File

@@ -9,7 +9,7 @@
以下是使用者在主線推進期間插入、且必須保留在完整工作項目裡的要求。後續不得再把這些需求漏掉或變成口頭提醒。
1. 正式環境才是最新版本真相;不得用本機檔案、舊筆記、舊分支蓋過 production truth。
2. 版本不得搞錯;目前正式環境 `/health` 版本維持 `V10.733`,除非有明確版本 bump、部署與 readback。
2. 版本不得搞錯;目前正式環境 `/health` 版本維持 `V10.734`,除非有明確版本 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.733`
- 正式環境版本真相 guard`/health` 是最高真相,目前維持 `V10.734`
- Gitea `dev` / `main` source truth 對齊。
- Retry exception controlled apply executor 已落地,目標表為 `pchome_product_matches`
- 正式 DB 已 controlled apply 4 個 selectors:
@@ -141,10 +141,17 @@
- `/api/ai-automation/scheduled-health-summary` 已輸出 `pchome_auto_policy_signing_issuer_closeout` family
- `/metrics` 已輸出 `momo_ai_automation_scheduled_health_family_status{family="pchome_auto_policy_signing_issuer_closeout",...}`
- 不讀 secret、不執行 SQL、不寫 DB、不簽發 apply authorization下一個機器動作是 signing execution preflight lane
- PChome auto-policy signing execution preflight runtime monitoring 已完成:
- `/api/ai-automation/smoke` 已加入 `PChome auto-policy signing execution preflight`
- 以本機 contract fixture + machine evidence adapter 自動推進 operator-held secret boundary preflight path
- 明確標記 `signing_execution_preflight_check_count=12``signing_issuer_closeout_check_count=12``operator_held_secret_boundary_count=1``signing_execution_input_requirement_count=10``signing_execution_abort_condition_count=8``rollback_boundary_count=4``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_signing_execution_preflight` family
- `/metrics` 已輸出 `momo_ai_automation_scheduled_health_family_status{family="pchome_auto_policy_signing_execution_preflight",...}`
- 不讀 secret、不執行 SQL、不寫 DB、不簽發 apply authorization下一個機器動作是 signing execution closeout lane
進行中 / 下一步,必須照順序:
1. 將 auto-policy signing issuer closeout 往 signing execution preflight lane 推進。
1. 將 auto-policy signing execution preflight 往 signing execution closeout lane 推進。
完成標準:
@@ -311,7 +318,7 @@
進行中 / 下一步,必須照順序:
1. 將 auto-policy signing issuer closeout 推進到 signing execution preflight lane。
1. 將 auto-policy signing execution preflight 推進到 signing execution closeout lane。
2. 對其他 safe mapping backlog lanes 套用 receipt / replay / drift verifier 模式。
3. 為每一類 controlled apply 自動產生 rollback evidence package。
4. 把 AI automation metrics 持續整合進既有 observability surfaces。
@@ -390,7 +397,8 @@
| P3.9 | PChome auto-policy signing decision preflight monitoring | 已完成 | AI smoke + scheduled family + metrics family prove signing decision preflight ready, signing inputs bound, no DB write, no signing, no apply authorization | P3.10 signing decision closeout monitoring |
| P3.10 | PChome auto-policy signing decision closeout monitoring | 已完成 | AI smoke + scheduled family + metrics family prove signing decision closeout ready, unsigned package bound, no DB write, no signing, no apply authorization | P3.11 signing issuer guard monitoring |
| P3.11 | PChome auto-policy signing issuer guard monitoring | 已完成 | AI smoke + scheduled family + metrics family prove signing issuer guard ready, future signable boundary bound, no DB write, no signing, no apply authorization | P3.12 signing issuer closeout monitoring |
| P3.12 | PChome auto-policy signing issuer closeout monitoring | 已完成 | AI smoke + scheduled family + metrics family prove final signable request package ready, no DB write, no signing, no apply authorization | P3.13 signing execution preflight lane |
| P3.12 | PChome auto-policy signing issuer closeout monitoring | 已完成 | AI smoke + scheduled family + metrics family prove final signable request package ready, no DB write, no signing, no apply authorization | P3.13 signing execution preflight monitoring |
| P3.13 | PChome auto-policy signing execution preflight monitoring | 已完成 | AI smoke + scheduled family + metrics family prove operator-held secret boundary, 10 nonsecret inputs, 8 abort conditions, 4 rollback boundaries, no secret read, no DB write, no signing | P3.14 signing execution closeout lane |
| 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 |

View File

@@ -297,6 +297,23 @@ def build_scheduled_automation_health_summary(
auto_policy_signing_issuer_closeout_details = (
auto_policy_signing_issuer_closeout.get("details") or {}
)
auto_policy_signing_execution_preflight = _find_check(
source_result,
"PChome auto-policy signing execution preflight",
)
auto_policy_signing_execution_preflight_details = (
auto_policy_signing_execution_preflight.get("details") or {}
)
if (
not auto_policy_signing_execution_preflight
or not auto_policy_signing_execution_preflight_details
):
auto_policy_signing_execution_preflight = (
_pchome_auto_policy_signing_execution_preflight_check()
)
auto_policy_signing_execution_preflight_details = (
auto_policy_signing_execution_preflight.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")
@@ -1246,6 +1263,207 @@ def build_scheduled_automation_health_summary(
"writes_database": False,
},
},
{
"key": "pchome_auto_policy_signing_execution_preflight",
"label": "PChome auto-policy signing execution preflight",
"status": auto_policy_signing_execution_preflight.get("status") or "warning",
"summary": (
auto_policy_signing_execution_preflight.get("summary")
or "PChome auto-policy signing execution preflight has no latest check."
),
"next_machine_action": auto_policy_signing_execution_preflight_details.get(
"next_machine_action"
)
or (
"continue_pchome_auto_policy_signing_execution_preflight_monitoring"
if auto_policy_signing_execution_preflight.get("status") == "ok"
else "refresh_pchome_auto_policy_signing_execution_preflight"
),
"details": {
"policy": auto_policy_signing_execution_preflight_details.get("policy"),
"result": auto_policy_signing_execution_preflight_details.get("result"),
"signing_execution_preflight_ready_count": int(
auto_policy_signing_execution_preflight_details.get(
"signing_execution_preflight_ready_count"
)
or 0
),
"signing_execution_preflight_check_count": int(
auto_policy_signing_execution_preflight_details.get(
"signing_execution_preflight_check_count"
)
or 0
),
"signing_execution_preflight_pass_count": int(
auto_policy_signing_execution_preflight_details.get(
"signing_execution_preflight_pass_count"
)
or 0
),
"signing_execution_preflight_waiting_count": int(
auto_policy_signing_execution_preflight_details.get(
"signing_execution_preflight_waiting_count"
)
or 0
),
"signing_issuer_closeout_ready_count": int(
auto_policy_signing_execution_preflight_details.get(
"signing_issuer_closeout_ready_count"
)
or 0
),
"signing_issuer_closeout_check_count": int(
auto_policy_signing_execution_preflight_details.get(
"signing_issuer_closeout_check_count"
)
or 0
),
"final_signable_request_package_ready_count": int(
auto_policy_signing_execution_preflight_details.get(
"final_signable_request_package_ready_count"
)
or 0
),
"operator_held_secret_boundary_count": int(
auto_policy_signing_execution_preflight_details.get(
"operator_held_secret_boundary_count"
)
or 0
),
"signing_execution_input_requirement_count": int(
auto_policy_signing_execution_preflight_details.get(
"signing_execution_input_requirement_count"
)
or 0
),
"signing_execution_abort_condition_count": int(
auto_policy_signing_execution_preflight_details.get(
"signing_execution_abort_condition_count"
)
or 0
),
"rollback_boundary_count": int(
auto_policy_signing_execution_preflight_details.get(
"rollback_boundary_count"
)
or 0
),
"post_apply_verifier_required_count": int(
auto_policy_signing_execution_preflight_details.get(
"post_apply_verifier_required_count"
)
or 0
),
"same_run_truth_required_count": int(
auto_policy_signing_execution_preflight_details.get(
"same_run_truth_required_count"
)
or 0
),
"reads_secret_count": int(
auto_policy_signing_execution_preflight_details.get(
"reads_secret_count"
)
or 0
),
"executes_script_count": int(
auto_policy_signing_execution_preflight_details.get(
"executes_script_count"
)
or 0
),
"executes_sql_count": int(
auto_policy_signing_execution_preflight_details.get(
"executes_sql_count"
)
or 0
),
"writes_database_count": int(
auto_policy_signing_execution_preflight_details.get(
"writes_database_count"
)
or 0
),
"signs_database_apply_authorization_count": int(
auto_policy_signing_execution_preflight_details.get(
"signs_database_apply_authorization_count"
)
or 0
),
"primary_human_gate_count": int(
auto_policy_signing_execution_preflight_details.get(
"primary_human_gate_count"
)
or 0
),
"manual_review_mode": auto_policy_signing_execution_preflight_details.get(
"manual_review_mode"
),
"payload_source": auto_policy_signing_execution_preflight_details.get(
"payload_source"
),
"execute_fetch": bool(
auto_policy_signing_execution_preflight_details.get("execute_fetch")
),
"outbound_network": bool(
auto_policy_signing_execution_preflight_details.get("outbound_network")
),
"business_data_source": bool(
auto_policy_signing_execution_preflight_details.get(
"business_data_source"
)
),
"secret_reference_mode": auto_policy_signing_execution_preflight_details.get(
"secret_reference_mode"
),
"command_preview_redacts_secret_values": bool(
auto_policy_signing_execution_preflight_details.get(
"command_preview_redacts_secret_values"
)
),
"command_preview_executes_in_preview": bool(
auto_policy_signing_execution_preflight_details.get(
"command_preview_executes_in_preview"
)
),
"ready_for_database_apply_now": bool(
auto_policy_signing_execution_preflight_details.get(
"ready_for_database_apply_now"
)
),
"issues_database_apply_authorization": bool(
auto_policy_signing_execution_preflight_details.get(
"issues_database_apply_authorization"
)
),
"signs_database_apply_authorization": bool(
auto_policy_signing_execution_preflight_details.get(
"signs_database_apply_authorization"
)
),
"secret_material_included": bool(
auto_policy_signing_execution_preflight_details.get(
"secret_material_included"
)
),
"secret_material_required_in_preview": bool(
auto_policy_signing_execution_preflight_details.get(
"secret_material_required_in_preview"
)
),
"ready_for_future_signing_execution_preflight": bool(
auto_policy_signing_execution_preflight_details.get(
"ready_for_future_signing_execution_preflight"
)
),
"permits_future_explicit_authorization_signing_execution_lane": bool(
auto_policy_signing_execution_preflight_details.get(
"permits_future_explicit_authorization_signing_execution_lane"
)
),
"writes_database": False,
},
},
{
"key": "ai_surface_html_readback",
"label": "AI surface HTML readback",
@@ -3472,6 +3690,318 @@ def _pchome_auto_policy_signing_issuer_closeout_check() -> Dict[str, Any]:
)
def _pchome_auto_policy_signing_execution_preflight_check() -> Dict[str, Any]:
"""Read-only monitor for future signing execution preflight readiness."""
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()
preflight = (
backlog.build_pchome_auto_policy_db_apply_authorization_signing_execution_preflight(
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 = preflight.get("summary") or {}
future_preflight = (
preflight.get("future_authorization_signing_execution_preflight") or {}
)
package = preflight.get("signing_execution_preflight_package") or {}
boundary = preflight.get("operator_held_secret_boundary_contract") or {}
contract = preflight.get("signing_execution_preflight_contract") or {}
safety = preflight.get("safety") or {}
command_preview = package.get("command_preview") or {}
result = str(preflight.get("result") or "UNKNOWN")
preflight_ready_count = int(
summary.get("authorization_signing_execution_preflight_ready_count") or 0
)
check_count = int(summary.get("signing_execution_preflight_check_count") or 0)
pass_count = int(summary.get("signing_execution_preflight_pass_count") or 0)
waiting_count = int(summary.get("signing_execution_preflight_waiting_count") or 0)
closeout_ready_count = int(
summary.get("authorization_signing_issuer_closeout_ready_count") or 0
)
closeout_check_count = int(summary.get("signing_issuer_closeout_check_count") or 0)
closeout_pass_count = int(summary.get("signing_issuer_closeout_pass_count") or 0)
final_package_ready_count = int(
summary.get("final_signable_request_package_ready_count") or 0
)
secret_boundary_count = int(summary.get("operator_held_secret_boundary_count") or 0)
input_requirement_count = int(
summary.get("signing_execution_input_requirement_count") or 0
)
abort_condition_count = int(
summary.get("signing_execution_abort_condition_count") or 0
)
rollback_boundary_count = int(summary.get("rollback_boundary_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,
future_preflight.get("ready_for_database_apply_now") is not False,
future_preflight.get("issues_database_apply_authorization") is not False,
future_preflight.get("signs_database_apply_authorization") is not False,
future_preflight.get("secret_material_included") is not False,
future_preflight.get("secret_material_required_in_preview") is not False,
future_preflight.get("reads_secret_in_preview") is not False,
package.get("ready_for_database_apply_now") is not False,
package.get("issues_database_apply_authorization") is not False,
package.get("signs_database_apply_authorization") is not False,
package.get("secret_material_included") is not False,
package.get("secret_material_required_in_preview") is not False,
package.get("reads_secret_in_preview") is not False,
package.get("executes_shell_in_preview") is not False,
package.get("executes_sql_in_preview") is not False,
package.get("writes_database_in_preview") is not False,
boundary.get("secret_material_included") is not False,
boundary.get("secret_material_required_in_preview") is not False,
boundary.get("reads_secret_in_preview") is not False,
boundary.get("accepts_plaintext_secret") is not False,
boundary.get("permits_secret_value_logging") is not False,
command_preview.get("executes_in_preview") is not False,
command_preview.get("signs_database_apply_authorization") is not False,
command_preview.get("writes_database") is not False,
contract.get("issues_database_apply_authorization") 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,
]
)
preflight_contract_present = (
preflight_ready_count == 1
and closeout_ready_count == 1
and check_count >= 12
and pass_count == check_count
and waiting_count == 0
and closeout_check_count == 12
and final_package_ready_count == 1
and secret_boundary_count == 1
and input_requirement_count == 10
and abort_condition_count == 8
and rollback_boundary_count == 4
and verifier_required_count == 1
and same_run_truth_count == 1
and future_preflight.get("ready_for_future_signing_execution_preflight")
is True
and (
future_preflight.get(
"can_enter_future_authorization_signing_execution_lane"
)
is True
)
and package.get("authorization_material_type")
== "signing_execution_preflight_package"
and package.get("ready_for_future_signing_execution_preflight") is True
and int(package.get("required_nonsecret_input_count") or 0) == 10
and package.get("hash_matches") is True
and package.get("requires_post_apply_verifier") is True
and package.get("requires_fresh_production_truth_in_same_run") is True
and boundary.get("secret_reference_mode")
== "external_runtime_reference_only"
and command_preview.get("mode") == "future_command_shape_only"
and command_preview.get("redacts_secret_values") is True
and command_preview.get("executes_in_preview") is False
and command_preview.get("signs_database_apply_authorization") is False
and command_preview.get("writes_database") is False
and contract.get("machine_verifiable") is True
and (
contract.get(
"permits_future_explicit_authorization_signing_execution_lane"
)
is True
)
and safety.get("manual_review_mode") == "exception_only"
)
if risk_detected:
status = "critical"
summary_text = (
"PChome auto-policy signing execution preflight 偵測到 secret、SQL、"
"DB write、簽發授權或人工主 gate 風險"
)
next_machine_action = (
"halt_pchome_auto_policy_signing_execution_preflight_and_inspect_risk"
)
elif preflight_contract_present:
status = "ok"
summary_text = (
"PChome auto-policy signing execution preflight 已自動完成 "
f"{pass_count}/{check_count} checksoperator-held secret boundary 已外部化authorization signing=0"
)
next_machine_action = "continue_to_pchome_auto_policy_signing_execution_closeout_lane"
else:
status = "warning"
summary_text = (
"PChome auto-policy signing execution preflight 尚未達自動監控契約"
)
next_machine_action = "refresh_pchome_auto_policy_signing_execution_preflight"
return _check(
"PChome auto-policy signing execution preflight",
status,
summary_text,
{
"policy": preflight.get("policy"),
"result": result,
"source_policy": preflight.get("source_policy"),
"signing_execution_preflight_ready_count": preflight_ready_count,
"signing_execution_preflight_check_count": check_count,
"signing_execution_preflight_pass_count": pass_count,
"signing_execution_preflight_waiting_count": waiting_count,
"signing_issuer_closeout_ready_count": closeout_ready_count,
"signing_issuer_closeout_check_count": closeout_check_count,
"signing_issuer_closeout_pass_count": closeout_pass_count,
"final_signable_request_package_ready_count": (
final_package_ready_count
),
"operator_held_secret_boundary_count": secret_boundary_count,
"signing_execution_input_requirement_count": input_requirement_count,
"signing_execution_abort_condition_count": abort_condition_count,
"rollback_boundary_count": rollback_boundary_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,
"secret_reference_mode": boundary.get("secret_reference_mode"),
"command_preview_redacts_secret_values": bool(
command_preview.get("redacts_secret_values")
),
"command_preview_executes_in_preview": bool(
command_preview.get("executes_in_preview")
),
"ready_for_database_apply_now": bool(
future_preflight.get("ready_for_database_apply_now")
or package.get("ready_for_database_apply_now")
or contract.get("ready_for_database_apply_now")
),
"issues_database_apply_authorization": bool(
future_preflight.get("issues_database_apply_authorization")
or package.get("issues_database_apply_authorization")
or contract.get("issues_database_apply_authorization")
),
"signs_database_apply_authorization": bool(
future_preflight.get("signs_database_apply_authorization")
or package.get("signs_database_apply_authorization")
or contract.get("signs_database_apply_authorization")
),
"secret_material_included": bool(
future_preflight.get("secret_material_included")
or package.get("secret_material_included")
or boundary.get("secret_material_included")
),
"secret_material_required_in_preview": bool(
future_preflight.get("secret_material_required_in_preview")
or package.get("secret_material_required_in_preview")
or boundary.get("secret_material_required_in_preview")
or contract.get("secret_material_required_in_preview")
),
"ready_for_future_signing_execution_preflight": bool(
future_preflight.get("ready_for_future_signing_execution_preflight")
),
"permits_future_explicit_authorization_signing_execution_lane": bool(
contract.get(
"permits_future_explicit_authorization_signing_execution_lane"
)
),
"requires_post_apply_verifier": bool(
package.get("requires_post_apply_verifier")
),
"requires_fresh_production_truth": bool(
package.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 signing execution preflight",
"warning",
f"PChome auto-policy signing execution preflight 例行監控暫時無法讀取:{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_signing_execution_preflight"
),
},
)
def _ai_surface_html_readback_check() -> Dict[str, Any]:
"""Read-only AI product surface guardrail readback."""
try:
@@ -3707,6 +4237,7 @@ def collect_ai_automation_smoke(*, record_history: bool = True, history_limit: i
_pchome_auto_policy_signing_decision_closeout_check(),
_pchome_auto_policy_signing_issuer_guard_check(),
_pchome_auto_policy_signing_issuer_closeout_check(),
_pchome_auto_policy_signing_execution_preflight_check(),
_ai_surface_html_readback_check(),
_sitewide_ui_ux_agent_check(),
_sitewide_visual_qa_check(),

View File

@@ -107,10 +107,10 @@ def test_system_metrics_exports_scheduled_health_summary():
Gauge,
{
"summary": {
"ok": 12,
"ok": 13,
"warning": 1,
"critical": 0,
"total": 13,
"total": 14,
"primary_human_gate_count": 0,
"writes_database_count": 0,
},
@@ -125,6 +125,7 @@ def test_system_metrics_exports_scheduled_health_summary():
{"key": "pchome_auto_policy_signing_decision_closeout", "status": "ok"},
{"key": "pchome_auto_policy_signing_issuer_guard", "status": "ok"},
{"key": "pchome_auto_policy_signing_issuer_closeout", "status": "ok"},
{"key": "pchome_auto_policy_signing_execution_preflight", "status": "ok"},
{"key": "sitewide_ui_ux_agent", "status": "ok"},
{"key": "sitewide_visual_qa", "status": "ok"},
],
@@ -132,7 +133,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"} 12.0' in output
assert 'momo_ai_automation_scheduled_health_summary_total{status="ok"} 13.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'
@@ -174,6 +175,10 @@ def test_system_metrics_exports_scheduled_health_summary():
'momo_ai_automation_scheduled_health_family_status{family="pchome_auto_policy_signing_issuer_closeout",status="ok"} 1.0'
in output
)
assert (
'momo_ai_automation_scheduled_health_family_status{family="pchome_auto_policy_signing_execution_preflight",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

View File

@@ -316,6 +316,59 @@ def _auto_policy_signing_issuer_closeout_history_check(status="ok"):
}
def _auto_policy_signing_execution_preflight_history_check(status="ok"):
return {
"name": "PChome auto-policy signing execution preflight",
"status": status,
"summary": "PChome auto-policy signing execution preflight 已自動完成",
"details": {
"policy": (
"read_only_pchome_growth_auto_policy_db_apply_authorization_"
"signing_execution_preflight"
),
"result": "DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT_READY",
"signing_execution_preflight_ready_count": 1,
"signing_execution_preflight_check_count": 12,
"signing_execution_preflight_pass_count": 12,
"signing_execution_preflight_waiting_count": 0,
"signing_issuer_closeout_ready_count": 1,
"signing_issuer_closeout_check_count": 12,
"signing_issuer_closeout_pass_count": 12,
"final_signable_request_package_ready_count": 1,
"operator_held_secret_boundary_count": 1,
"signing_execution_input_requirement_count": 10,
"signing_execution_abort_condition_count": 8,
"rollback_boundary_count": 4,
"post_apply_verifier_required_count": 1,
"same_run_truth_required_count": 1,
"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,
"manual_review_mode": "exception_only",
"payload_source": "local_contract_fixture",
"execute_fetch": True,
"outbound_network": False,
"business_data_source": False,
"secret_reference_mode": "external_runtime_reference_only",
"command_preview_redacts_secret_values": True,
"command_preview_executes_in_preview": 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,
"ready_for_future_signing_execution_preflight": True,
"permits_future_explicit_authorization_signing_execution_lane": True,
"next_machine_action": (
"continue_to_pchome_auto_policy_signing_execution_closeout_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
@@ -359,6 +412,7 @@ def test_collect_ai_automation_smoke_uses_worst_status(monkeypatch):
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_decision_closeout_check", lambda: smoke._check("auto-policy signing closeout", "ok", "ok"))
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_issuer_guard_check", lambda: smoke._check("auto-policy signing issuer guard", "ok", "ok"))
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_issuer_closeout_check", lambda: smoke._check("auto-policy signing issuer closeout", "ok", "ok"))
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_execution_preflight_check", lambda: smoke._check("auto-policy signing execution preflight", "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"))
@@ -366,7 +420,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": 16, "warning": 1, "critical": 1, "total": 18}
assert result["summary"] == {"ok": 17, "warning": 1, "critical": 1, "total": 19}
def test_pchome_controlled_apply_drift_monitor_reports_verified_zero_drift(monkeypatch):
@@ -1156,6 +1210,161 @@ def test_pchome_auto_policy_signing_issuer_closeout_reports_final_signable_no_wr
)
def test_pchome_auto_policy_signing_execution_preflight_reports_operator_secret_boundary_no_write(monkeypatch):
from services import ai_automation_smoke_service as smoke
from services import pchome_mapping_backlog_service as backlog
captured = {}
def fake_preflight(*_args, **kwargs):
captured.update(kwargs)
return {
"policy": (
"read_only_pchome_growth_auto_policy_db_apply_authorization_"
"signing_execution_preflight"
),
"result": "DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT_READY",
"source_policy": (
"read_only_pchome_growth_auto_policy_db_apply_authorization_"
"signing_issuer_closeout"
),
"summary": {
"authorization_signing_execution_preflight_ready_count": 1,
"signing_execution_preflight_check_count": 12,
"signing_execution_preflight_pass_count": 12,
"signing_execution_preflight_waiting_count": 0,
"authorization_signing_issuer_closeout_ready_count": 1,
"signing_issuer_closeout_check_count": 12,
"signing_issuer_closeout_pass_count": 12,
"final_signable_request_package_ready_count": 1,
"operator_held_secret_boundary_count": 1,
"signing_execution_input_requirement_count": 10,
"signing_execution_abort_condition_count": 8,
"rollback_boundary_count": 4,
"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,
},
"future_authorization_signing_execution_preflight": {
"ready_for_future_signing_execution_preflight": True,
"can_enter_future_authorization_signing_execution_lane": True,
"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,
"reads_secret_in_preview": False,
},
"signing_execution_preflight_package": {
"authorization_material_type": "signing_execution_preflight_package",
"ready_for_future_signing_execution_preflight": True,
"ready_for_database_apply_now": False,
"issues_database_apply_authorization": False,
"signs_database_apply_authorization": False,
"required_nonsecret_input_count": 10,
"hash_matches": True,
"requires_post_apply_verifier": True,
"requires_fresh_production_truth_in_same_run": True,
"secret_material_included": False,
"secret_material_required_in_preview": False,
"reads_secret_in_preview": False,
"executes_shell_in_preview": False,
"executes_sql_in_preview": False,
"writes_database_in_preview": False,
"command_preview": {
"mode": "future_command_shape_only",
"redacts_secret_values": True,
"executes_in_preview": False,
"signs_database_apply_authorization": False,
"writes_database": False,
},
},
"operator_held_secret_boundary_contract": {
"secret_reference_mode": "external_runtime_reference_only",
"secret_material_included": False,
"secret_material_required_in_preview": False,
"reads_secret_in_preview": False,
"accepts_plaintext_secret": False,
"permits_secret_value_logging": False,
},
"signing_execution_preflight_contract": {
"machine_verifiable": True,
"permits_future_explicit_authorization_signing_execution_lane": True,
"issues_database_apply_authorization": False,
"ready_for_database_apply_now": False,
"signs_database_apply_authorization": False,
"writes_database": False,
"executes_in_preview": False,
"secret_material_required_in_preview": False,
},
"safety": {
"reads_secret_in_preview": False,
"writes_file": False,
"writes_script_in_preview": False,
"writes_artifact_in_preview": False,
"executes_script": False,
"executes_endpoint": False,
"executes_migration": False,
"executes_sql": False,
"writes_database": False,
"signs_database_apply_authorization": False,
"manual_review_mode": "exception_only",
},
}
monkeypatch.setattr(
backlog,
"build_pchome_auto_policy_db_apply_authorization_signing_execution_preflight",
fake_preflight,
)
result = smoke._pchome_auto_policy_signing_execution_preflight_check()
assert result["status"] == "ok"
assert captured["execute_fetch"] is True
assert captured["http_get"] is smoke._pchome_auto_policy_machine_fetch_evidence_get
assert result["details"]["signing_execution_preflight_pass_count"] == 12
assert result["details"]["signing_issuer_closeout_check_count"] == 12
assert result["details"]["final_signable_request_package_ready_count"] == 1
assert result["details"]["operator_held_secret_boundary_count"] == 1
assert result["details"]["signing_execution_input_requirement_count"] == 10
assert result["details"]["signing_execution_abort_condition_count"] == 8
assert result["details"]["rollback_boundary_count"] == 4
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"]["reads_secret_count"] == 0
assert result["details"]["writes_database_count"] == 0
assert result["details"]["signs_database_apply_authorization_count"] == 0
assert result["details"]["secret_reference_mode"] == "external_runtime_reference_only"
assert result["details"]["command_preview_redacts_secret_values"] is True
assert result["details"]["command_preview_executes_in_preview"] is False
assert result["details"]["issues_database_apply_authorization"] is False
assert result["details"]["signs_database_apply_authorization"] is False
assert result["details"]["secret_material_included"] is False
assert result["details"]["secret_material_required_in_preview"] is False
assert result["details"]["ready_for_future_signing_execution_preflight"] is True
assert (
result["details"]["permits_future_explicit_authorization_signing_execution_lane"]
is True
)
assert result["details"]["next_machine_action"] == (
"continue_to_pchome_auto_policy_signing_execution_closeout_lane"
)
def test_collect_ai_automation_smoke_persists_recent_history(tmp_path, monkeypatch):
from services import ai_automation_smoke_service as smoke
@@ -1177,6 +1386,7 @@ def test_collect_ai_automation_smoke_persists_recent_history(tmp_path, monkeypat
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_decision_closeout_check", lambda: smoke._check("auto-policy signing closeout", "ok", "ok"))
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_issuer_guard_check", lambda: smoke._check("auto-policy signing issuer guard", "ok", "ok"))
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_issuer_closeout_check", lambda: smoke._check("auto-policy signing issuer closeout", "ok", "ok"))
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_execution_preflight_check", lambda: smoke._check("auto-policy signing execution preflight", "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"))
@@ -1235,7 +1445,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": 18, "warning": 0, "critical": 0, "total": 18},
"summary": {"ok": 19, "warning": 0, "critical": 0, "total": 19},
"checks": [
{
"name": "PChome 受控落地 drift monitor",
@@ -1257,6 +1467,7 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects(
_auto_policy_signing_decision_closeout_history_check(),
_auto_policy_signing_issuer_guard_history_check(),
_auto_policy_signing_issuer_closeout_history_check(),
_auto_policy_signing_execution_preflight_history_check(),
{
"name": "AI surface HTML readback",
"status": "ok",
@@ -1318,7 +1529,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"] == 15
assert summary["summary"]["total"] == 16
assert summary["summary"]["primary_human_gate_count"] == 0
assert summary["summary"]["writes_database_count"] == 0
assert pchome_family["status"] == "ok"
@@ -1478,6 +1689,93 @@ def test_scheduled_automation_health_summary_reads_history_without_side_effects(
]
is True
)
signing_execution_preflight_family = next(
item for item in summary["families"]
if item["key"] == "pchome_auto_policy_signing_execution_preflight"
)
assert signing_execution_preflight_family["status"] == "ok"
assert (
signing_execution_preflight_family["details"][
"signing_execution_preflight_pass_count"
]
== 12
)
assert (
signing_execution_preflight_family["details"][
"signing_issuer_closeout_check_count"
]
== 12
)
assert (
signing_execution_preflight_family["details"][
"final_signable_request_package_ready_count"
]
== 1
)
assert (
signing_execution_preflight_family["details"][
"operator_held_secret_boundary_count"
]
== 1
)
assert (
signing_execution_preflight_family["details"][
"signing_execution_input_requirement_count"
]
== 10
)
assert (
signing_execution_preflight_family["details"][
"signing_execution_abort_condition_count"
]
== 8
)
assert signing_execution_preflight_family["details"]["rollback_boundary_count"] == 4
assert (
signing_execution_preflight_family["details"]["payload_source"]
== "local_contract_fixture"
)
assert signing_execution_preflight_family["details"]["outbound_network"] is False
assert signing_execution_preflight_family["details"]["business_data_source"] is False
assert signing_execution_preflight_family["details"]["primary_human_gate_count"] == 0
assert signing_execution_preflight_family["details"]["reads_secret_count"] == 0
assert signing_execution_preflight_family["details"]["writes_database_count"] == 0
assert (
signing_execution_preflight_family["details"][
"signs_database_apply_authorization_count"
]
== 0
)
assert (
signing_execution_preflight_family["details"]["secret_reference_mode"]
== "external_runtime_reference_only"
)
assert (
signing_execution_preflight_family["details"][
"command_preview_redacts_secret_values"
]
is True
)
assert (
signing_execution_preflight_family["details"][
"command_preview_executes_in_preview"
]
is False
)
assert (
signing_execution_preflight_family["details"]["signs_database_apply_authorization"]
is False
)
assert (
signing_execution_preflight_family["details"]["secret_material_included"]
is False
)
assert (
signing_execution_preflight_family["details"][
"ready_for_future_signing_execution_preflight"
]
is True
)
surface_family = next(
item for item in summary["families"]
if item["key"] == "ai_surface_html_readback"
@@ -1512,7 +1810,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": 17, "warning": 0, "critical": 1, "total": 18},
"summary": {"ok": 18, "warning": 0, "critical": 1, "total": 19},
"checks": [
{
"name": "PChome 受控落地 drift monitor",
@@ -1528,6 +1826,7 @@ def test_scheduled_automation_health_summary_can_use_current_smoke_without_recor
_auto_policy_signing_decision_closeout_history_check(),
_auto_policy_signing_issuer_guard_history_check(),
_auto_policy_signing_issuer_closeout_history_check(),
_auto_policy_signing_execution_preflight_history_check(),
{
"name": "AI surface HTML readback",
"status": "ok",
@@ -1583,7 +1882,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": 16, "warning": 0, "critical": 0, "total": 16},
"summary": {"ok": 17, "warning": 0, "critical": 0, "total": 17},
"checks": [],
}, ensure_ascii=False) + "\n",
encoding="utf-8",
@@ -1629,6 +1928,11 @@ def test_scheduled_automation_health_summary_falls_back_to_visual_qa_artifact(tm
"_pchome_auto_policy_signing_issuer_closeout_check",
lambda: _auto_policy_signing_issuer_closeout_history_check(),
)
monkeypatch.setattr(
smoke,
"_pchome_auto_policy_signing_execution_preflight_check",
lambda: _auto_policy_signing_execution_preflight_history_check(),
)
monkeypatch.setattr(
surface_service,
"build_sitewide_visual_qa_readback",
@@ -1677,7 +1981,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": 17, "warning": 0, "critical": 0, "total": 17},
"summary": {"ok": 18, "warning": 0, "critical": 0, "total": 18},
"checks": [],
}, ensure_ascii=False) + "\n",
encoding="utf-8",
@@ -1723,6 +2027,11 @@ def test_scheduled_automation_health_summary_route_returns_compact_payload(tmp_p
"_pchome_auto_policy_signing_issuer_closeout_check",
lambda: _auto_policy_signing_issuer_closeout_history_check(),
)
monkeypatch.setattr(
smoke,
"_pchome_auto_policy_signing_execution_preflight_check",
lambda: _auto_policy_signing_execution_preflight_history_check(),
)
app = Flask(__name__)
with app.test_request_context(
@@ -1954,6 +2263,7 @@ def test_surface_html_readback_check_is_part_of_ai_smoke(monkeypatch):
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_decision_closeout_check", lambda: smoke._check("auto-policy signing closeout", "ok", "ok"))
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_issuer_guard_check", lambda: smoke._check("auto-policy signing issuer guard", "ok", "ok"))
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_issuer_closeout_check", lambda: smoke._check("auto-policy signing issuer closeout", "ok", "ok"))
monkeypatch.setattr(smoke, "_pchome_auto_policy_signing_execution_preflight_check", lambda: smoke._check("auto-policy signing execution preflight", "ok", "ok"))
monkeypatch.setattr(smoke, "_sitewide_visual_qa_check", lambda: smoke._check(
"Sitewide visual QA readback",
"ok",
@@ -1975,7 +2285,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"] == 18
assert result["summary"]["total"] == 19
assert surface_check["status"] == "ok"
assert surface_check["details"]["checked_surface_count"] == 10
assert sitewide_check["status"] == "ok"